Skip to main content
CoinVoyage v3 is a breaking API and SDK update. The main change is the move from PayOrder terminology and helper names to Order terminology across the REST API, PayKit, webhooks, and WebSocket events.
v3 upgrade is breaking. Keep your v2 integration running until you have tested order creation, payment completion, webhooks, refunds, swaps, and any off-ramp workflows in your target environment.

What changes

Areav2v3
API base URLhttps://api.coinvoyage.io/v2https://api.coinvoyage.io/v3
Primary resource namePayOrderOrder
Server-created PayButton proppayIdorderId
PayButton lifecycle start callbackonPaymentStartedonAwaitingPayment, onConfirmingPayment, onExecutingPayment
Webhook payload event fieldtype with lowercase payorder_* valuesevent with uppercase ORDER_* values
Webhook payload resource IDpayorder_idorder.id

Migration checklist

  1. Upgrade CoinVoyage dependencies to v3, including @coin-voyage/paykit@3.0.0.
  2. Update ApiClient PayOrder helper names with the v3 Order helper names.
  3. Update PayButton and PayButton.Custom from payId to orderId.
  4. Replace onPaymentStarted with the more specific payment lifecycle callbacks.
  5. Update webhook handlers to read { event, delivered_at, order }.
  6. Run a full test payment and verify that webhook fulfillment remains idempotent.

Upgrade PayKit

pnpm add @coin-voyage/paykit@3.0.0
Use the package manager your project already uses.

Update ApiClient initialization

ApiClient is now called as a factory function from @coin-voyage/paykit/server.
import { ApiClient } from "@coin-voyage/paykit/server";

const apiClient = ApiClient({
  apiKey: process.env.COIN_VOYAGE_API_KEY!,
  environment: "production",
});
When you manually sign HTTP requests, sign the path without the /v3 prefix:
const authorization = apiClient.generateAuthorizationSignature(
  process.env.COIN_VOYAGE_API_SECRET!,
  "POST",
  "/orders"
);

Rename order methods

Replace v2 PayOrder helper names with v3 Order helper names.
v2 methodv3 method
createDepositPayOrder(params)createDepositOrder(params)
createSalePayOrder(params, apiSecret)createSaleOrder(params, apiSecret)
createRefundPayOrder(orderId, params, apiSecret)createRefundOrder(orderId, params, apiSecret)
getPayOrder(payOrderId)getOrder(orderId)
listPayOrders(params, apiSecret)listOrders(params, apiSecret)
payOrderQuote(orderId, params)orderQuotes(orderId, params)
payOrderPaymentDetails(params)createPayment(orderId, params)
getPayOrderPaymentMethods(orderId)getOrderPaymentMethods(orderId)

Deposit order before and after

const { data, error } = await apiClient.createDepositPayOrder({
  intent: {
    asset: {
      chain_id: ChainId.SUI,
      address: null,
    },
    amount: {
      token_amount: 10,
    },
    receiving_address: "0xDestinationWallet",
  },
});

Sale order before and after

const { data, error } = await apiClient.createSalePayOrder(
  {
    intent: {
      amount: {
        fiat: {
          amount: 49.99,
          unit: "USD",
        },
      },
    },
    metadata: {
      order_id: "order_123",
    },
  },
  process.env.COIN_VOYAGE_API_SECRET!
);

Update PayButton

Use orderId for server-created orders. Client-side deposit buttons still use toChain, toToken, toAmount, and toAddress.
<PayButton.Custom
  payId={payId}
  onPaymentStarted={(event) => {
    console.log("Payment started", event);
  }}
  onPaymentCompleted={(event) => {
    console.log("Payment completed", event);
  }}
>
  {({ show }) => <button onClick={show}>Pay</button>}
</PayButton.Custom>
The split callbacks give you clearer UI states:
v3 callbackWhen it fires
onAwaitingPaymentPayment details are available and the user can fund the order.
onConfirmingPaymentThe source transaction is detected and waiting for confirmation.
onExecutingPaymentCoinVoyage is executing the destination transfer or contract call.
onPaymentCompletedThe order completed successfully.
onPaymentBouncedThe destination call reverted and funds were refunded.

Update provider configuration

PayKitProvider no longer documents the v2 theme preset prop or options.embedGoogleFonts. Use mode and customTheme instead.
<PayKitProvider
  apiKey={process.env.NEXT_PUBLIC_COIN_VOYAGE_API_KEY!}
  theme="midnight"
  options={{
    language: "en",
    embedGoogleFonts: true,
  }}
>
  {children}
</PayKitProvider>
For Sui wallet configuration, rename config.sui.rpcUrl to config.sui.grpcUrl:
<WalletProvider
  config={{
    sui: {
      grpcUrl: "https://fullnode.mainnet.sui.io:443",
    },
  }}
>
  {children}
</WalletProvider>

Update swaps

Standalone swaps still use swapQuote() for the quote step. Use swapExecute() for execution data.
const { data, error } = await apiClient.swapData(params);

Update off-ramp flows

v3 names the fiat payout workflow as off-ramp verification, bank accounts, and off-ramp intents.
v2 methodv3 method
createKYCLink(params, apiSecret)createOffRampVerification(params, apiSecret)
getKYCStatus(apiSecret)getOffRampVerificationStatus(apiSecret)
listWithdrawals(apiSecret)listOffRampIntents(apiSecret)
createWithdrawal(params, apiSecret)createOffRampIntent(params, apiSecret)
Bank-account helpers remain part of the off-ramp workflow: listBankAccounts(apiSecret), getBankAccount(bankAccountId, apiSecret), and addBankAccount(params, apiSecret).

Update webhooks and WebSockets

v3 webhook and /v3/ws deliveries use the same event envelope:
{
  "event": "ORDER_COMPLETED",
  "delivered_at": "2026-06-23T12:34:56Z",
  "order": {
    "id": "cabc1234567890abcdef12",
    "mode": "SALE",
    "status": "COMPLETED",
    "metadata": {
      "order_id": "order_123"
    }
  }
}
Update handlers that previously read lowercase type values such as payorder_completed.
switch (payload.type) {
  case "payorder_completed":
    await fulfill(payload.payorder_id);
    break;
}
Use these v3 event identifiers when subscribing to or dispatching lifecycle events:
  • ORDER_CREATED
  • ORDER_AWAITING_PAYMENT
  • ORDER_CONFIRMING
  • ORDER_EXECUTING
  • ORDER_COMPLETED
  • ORDER_ERROR
  • ORDER_REFUNDED
  • ORDER_EXPIRED
  • ORDER_PARTIAL_PAYMENT

Test before switching production traffic

Before you route production users to v3:
  • Create a DEPOSIT order and verify the destination wallet receives funds.
  • Create a server-side SALE order and render it with orderId.
  • Verify ORDER_COMPLETED, failed, expired, refunded, and partial-payment webhook handling.
  • Run a refund through createRefundOrder().
  • Run swapQuote() and swapExecute() if your product exposes standalone swaps.
  • Run the off-ramp verification and off-ramp intent flow if you use fiat payouts.
  • Confirm your webhook handler is idempotent by event, delivered_at, order.id, and your internal ID in order.metadata.

Reference pages