Skip to main content
This guide walks you through a complete card-not-present (online) payment flow using the direct API integration. You create a Payment Intent, handle authentication if required, and receive the payment result.

Step 1: Create a Payment Intent

There are two approaches depending on when the payment method is available:
Include payment_method in the creation request. The payment processes immediately.
API reference: Create Payment Intent

Step 2: Handle the response

Check the intent_status field in the response to determine what to do next:

Successful response example

Step 3: Handle customer actions

When intent_status is REQUIRES_CUSTOMER_ACTION, the next_action field tells you what the customer needs to do.

3DS redirect

If the card requires 3D Secure authentication, next_action contains a redirect URL:
Redirect the customer to the URL. After they complete 3DS, they are redirected to your return_url. For detailed 3DS integration guidance, see:

QR code display

For e-wallet payments (AlipayCN, WeChatPay, etc.), next_action may contain a QR code:
Display the QR code to the customer and wait for them to scan and confirm payment in their wallet app.

Step 4: Get payment results

Use one or both of these methods to confirm the final payment status: Configure your server to receive asynchronous notifications from UQPAY. This is the most reliable method. Key webhook events:

Retrieve API (polling)

Query the payment status directly:
API reference: Retrieve Payment Intent

E-wallet payments

The same API supports e-wallet payment methods. Replace the payment_method object with the appropriate wallet type:
The response will contain next_action.display_qr_code for the customer to scan.
Payment code transactions complete immediately when the code is valid.
For a full list of supported payment methods and their parameters, see Payment methods.

Pre-authorization and manual capture

Before using pre-authorization, confirm that your account and payment channel support it. Use the direct API with card or card_present; Hosted Payment Page and Payment Links do not support pre-authorization.
  1. Set authorization_type to pre_authorization when you create or confirm the PaymentIntent. Set payment_method.card.auto_capture or payment_method.card_present.auto_capture to false. If you also provide the authorization type inside the payment method, both values must match. A pre-authorized PaymentIntent cannot be changed to ordinary authorization at confirmation.
  2. After authorization, inspect intent_status, authorization_expires_at, and available_to_capture. Capture while the PaymentIntent is REQUIRES_CAPTURE and before the returned expiry. Do not assume a fixed authorization period.
  3. Capture the PaymentIntent once. Omit final_capture or set it to true; false is rejected. Omit amount_to_capture to capture the available amount, or supply a positive amount up to that balance. Partial final capture requires channel support. Zero, empty, and null amounts are rejected. The system handles any remaining authorization release; do not send a second capture or a zero-amount capture to release it.
  4. Keep the returned capture_id and merchant_capture_order_id. Retrieve the PaymentIntent to inspect capture_records. Capture acceptance and the PaymentIntent aggregate amount do not establish channel completion. Each record has a channel_capture_status: 0 pending, 1 processing, 2 completed, or 3 failed.
For ordinary authorization with manual capture, the capture amount must equal the full authorized amount. Pre-authorization is the flow that permits a smaller final amount when supported.

Capture references and retries

Use an account-unique merchant_capture_order_id of at most 64 characters. If omitted, it defaults to the PaymentIntent’s merchant_order_id. Keep the same X-Idempotency-Key when retrying an identical request after a transport failure. A recorded capture reference submitted again outside the cached response is rejected, not executed as another capture. Query the existing PaymentIntent to determine the result before taking further action. A failed capture does not permit another capture against the same authorization.

Cancellation and refunds

Before any capture, you can cancel an unexpired pre-authorization when the channel supports cancellation. After capture, use the refund flow for captured funds. A positive available_to_void value alone does not establish cancellation eligibility. PaymentIntent captured_amount is the aggregate capture amount; PaymentAttempt has no capture-amount field. Use capture_records[].capture_amount and the associated processing status for individual captures.