ShopeePay App Redirection

Topics covered on this page

ShopeePay App-to-App Redirection (Jump App)

Accept online payments from ShopeePay users through your website using the ShopeePay App-to-App Redirection (Jump App) payment method.

This guide walks you through the payment flow and details how to implement it.


How to enable

  • Supported countries: Thailand, Singapore, Malaysia
  • Minimum API version: 2017-11-02

To enable ShopeePay App Redirection, send an email requesting this feature to support@omise.co. You will need to review and accept new terms and conditions.


Payment flow

Customers paying via ShopeePay App Redirection go through an app redirect payment flow. Unlike the standard ShopeePay redirect flow (which opens a browser-based QR page), this method redirects the customer directly into the ShopeePay mobile application to authorize and confirm the payment.

The following screenshots demonstrate this flow:

ShopeePay App Redirection payment flow

❶ The customer chooses ShopeePay as their payment method. ❷ They are redirected directly into the ShopeePay app to confirm the payment. ❸ The payment details are shown. ❹ The customer can return to the merchant's confirmation page by pressing Back to merchant. ❺ The customer is redirected back to the merchant's page.

ShopeePay App Redirection supports the following payment methods by country:

Payment method Thailand Singapore Malaysia
Wallet Balance
Credit Card
Direct Debit / Bank Account
SPayLater

Implementation

To create a charge using ShopeePay App Redirection, make the following API requests:

  1. Create a payment source (type: shopeepay_jumpapp) using Omise.js or one of the mobile SDKs (iOS or Android).
  2. Create a charge using the source identifier from step 1.
  3. After receiving the charge completion webhook event, retrieve the charge to verify its status (optional, but recommended).

Use your public key to create the ShopeePay App Redirection source on the client (a customer's browser or mobile phone). Use your secret key to create the ShopeePay App Redirection charge on the server.

If both source creation and charge must happen server-side, you can combine them in a single API request using your secret key.

Creating a source

When the customer confirms they wish to pay with ShopeePay App Redirection, create a source specifying amount, currency, type, and optionally platform_type.

Parameter Type Description
amount integer (Required) See Limits
currency string (Required) THB for Thailand, SGD for Singapore, MYR for Malaysia
type string (Required) shopeepay_jumpapp
platform_type string (Optional) The customer's device platform: IOS or ANDROID. When provided, the redirect is optimised for that platform.

The following examples demonstrate creating a ShopeePay App Redirection source for S$50. Replace omise_public_key and $OMISE_PUBLIC_KEY with the test public key from your dashboard.

In Omise.js, the type parameter is the first argument passed to the createSource method.

Omise.setPublicKey(omise_public_key);

Omise.createSource('shopeepay_jumpapp', {
  "amount": 5000,
  "currency": "SGD"
}, function(statusCode, response) {
  console.log(response);
});

For testing, you can make the same request using curl:

curl https://api.omise.co/sources \
  -u $OMISE_PUBLIC_KEY: \
  -d "amount=5000" \
  -d "currency=SGD" \
  -d "type=shopeepay_jumpapp"

Example response:

{
  "object": "source",
  "id": "src_test_62784yixw757rznv0pa",
  "livemode": false,
  "location": "/sources/src_test_62784yixw757rznv0pa",
  "amount": 5000,
  "barcode": null,
  "bank": null,
  "created_at": "2024-12-26T07:19:58Z",
  "currency": "SGD",
  "email": null,
  "flow": "app_redirect",
  "installment_term": null,
  "ip": "35.198.236.178",
  "absorption_type": null,
  "name": null,
  "mobile_number": null,
  "phone_number": null,
  "platform_type": null,
  "scannable_code": null,
  "billing": null,
  "shipping": null,
  "items": [],
  "references": null,
  "provider_references": null,
  "store_id": null,
  "store_name": null,
  "terminal_id": null,
  "type": "shopeepay_jumpapp",
  "zero_interest_installments": null,
  "charge_status": "unknown",
  "receipt_amount": null,
  "discounts": [],
  "promotion_code": null
}

The id attribute is the source identifier (begins with src). Note that flow is app_redirect for this payment method, distinguishing it from the redirect flow used by standard ShopeePay.

Creating a charge

Create a charge specifying return_uri, source, amount, and currency:

  • return_uri — the URL on your website to which the customer is redirected after completing payment authorization.
  • source — the source identifier returned in the previous step.
  • amount and currency — must match the values used when creating the source.

Omise recommends not using the charge attributes net, fee, fee_vat, and transaction_fees until the charge status is successful.

Replace $OMISE_SECRET_KEY with the test secret key from your dashboard. Replace $SOURCE_ID with the id of the source.

curl https://api.omise.co/charges \
  -u $OMISE_SECRET_KEY: \
  -d "amount=5000" \
  -d "currency=SGD" \
  -d "return_uri=https://example.com/orders/345678/complete" \
  -d "source=$SOURCE_ID"

Example response:

{
  "object": "charge",
  "id": "chrg_test_62784ym7mj3s0f70xdm",
  "location": "/charges/chrg_test_62784ym7mj3s0f70xdm",
  "amount": 5000,
  "acquirer_reference_number": null,
  "net": 0,
  "fee": 0,
  "fee_vat": 0,
  "interest": 0,
  "interest_vat": 0,
  "funding_amount": 5000,
  "refunded_amount": 0,
  "transaction_fees": {
    "fee_flat": null,
    "fee_rate": null,
    "vat_rate": "0.0"
  },
  "platform_fee": {
    "fixed": null,
    "amount": null,
    "percentage": null
  },
  "currency": "SGD",
  "funding_currency": "SGD",
  "ip": null,
  "refunds": {
    "object": "list",
    "data": [],
    "limit": 20,
    "offset": 0,
    "total": 0,
    "location": "/charges/chrg_test_62784ym7mj3s0f70xdm/refunds",
    "order": "chronological",
    "from": "1970-01-01T00:00:00Z",
    "to": "2024-12-26T07:19:59Z"
  },
  "link": null,
  "description": null,
  "metadata": {},
  "card": null,
  "source": {
    "object": "source",
    "id": "src_test_62784y966l3i97hq1nq",
    "livemode": false,
    "location": "/sources/src_test_62784y966l3i97hq1nq",
    "amount": 5000,
    "barcode": null,
    "bank": null,
    "created_at": "2024-12-26T07:19:57Z",
    "currency": "SGD",
    "email": null,
    "flow": "app_redirect",
    "installment_term": null,
    "ip": "35.198.236.178",
    "absorption_type": null,
    "name": null,
    "mobile_number": null,
    "phone_number": null,
    "platform_type": null,
    "scannable_code": null,
    "billing": null,
    "shipping": null,
    "items": [],
    "references": null,
    "provider_references": null,
    "store_id": null,
    "store_name": null,
    "terminal_id": null,
    "type": "shopeepay_jumpapp",
    "zero_interest_installments": null,
    "charge_status": "pending",
    "receipt_amount": null,
    "discounts": [],
    "promotion_code": null
  },
  "schedule": null,
  "linked_account": null,
  "customer": null,
  "dispute": null,
  "transaction": null,
  "failure_code": null,
  "failure_message": null,
  "merchant_advice": null,
  "status": "pending",
  "authorize_uri": "https://pay.omise.co/payments/pay2_test_62784ym9apoe3i9ozoh/authorize?acs=false",
  "return_uri": "https://example.com/orders/345678/complete",
  "created_at": "2024-12-26T07:19:58Z",
  "paid_at": null,
  "authorized_at": null,
  "expires_at": "2025-01-02T07:19:58Z",
  "expired_at": null,
  "reversed_at": null,
  "multi_capture": false,
  "zero_interest_installments": false,
  "branch": null,
  "terminal": null,
  "device": null,
  "authorized": false,
  "capturable": false,
  "capture": true,
  "disputable": false,
  "livemode": false,
  "refundable": false,
  "partially_refundable": false,
  "reversed": false,
  "reversible": false,
  "voided": false,
  "paid": false,
  "expired": false,
  "can_perform_void": false,
  "approval_code": null
}

Creating a source and charge

Alternatively, you can create and charge a source in a single API request using your secret key:

curl https://api.omise.co/charges \
  -u $OMISE_SECRET_KEY: \
  -d "amount=5000" \
  -d "currency=SGD" \
  -d "return_uri=https://example.com/orders/345678/complete" \
  -d "source[type]=shopeepay_jumpapp"

Setting the charge to expire

By default, a ShopeePay App Redirection charge expires 20 minutes after creation. You can set a custom expiry of up to 60 minutes by passing a UTC timestamp in the expires_at field at charge creation:

curl https://api.omise.co/charges \
  -u $OMISE_SECRET_KEY: \
  -d "amount=5000" \
  -d "currency=SGD" \
  -d "return_uri=https://example.com/orders/345678/complete" \
  -d "source[type]=shopeepay_jumpapp" \
  -d "expires_at=2020-07-01T15:00:00Z"

If you need to expire a charge before its scheduled expiry, use the following request:

curl https://api.omise.co/charges/$CHARGE_ID/expire \
  -X POST \
  -u $OMISE_SECRET_KEY:

Replace $CHARGE_ID with the id of the charge.

Completing the charge

A newly created charge has its status set to pending. Other possible values are successful, failed, and expired.

The following sequence diagram illustrates the full payment flow:

sequenceDiagram participant Customer participant OmiseJS participant Merchant participant OmiseAPI Customer->>OmiseJS: Send payment details for purchase OmiseJS->>OmiseAPI: Request source using payment details OmiseAPI-->>OmiseJS: Return source OmiseJS->>Merchant: Merchant receives source Merchant->>OmiseAPI: Request charge using source and purchase details OmiseAPI-->>Merchant: Send "charge.create" webhook OmiseAPI-->>Merchant: Return charge Merchant->>Customer: Redirect to authorize_uri for pending charge Customer->>OmiseAPI: Authorize charge at authorize_uri OmiseAPI-->>Customer: Redirect to return_uri OmiseAPI-->>Merchant: Send "charge.complete" webhook Merchant-->>Customer: Send charge result (e.g., via email)

Authorizing the charge

Redirect the customer to the URL specified in authorize_uri. This redirects the customer directly into the ShopeePay mobile app to authorize the charge. After authorization, the customer is redirected to the URL specified in return_uri.

You can simulate authorization in test mode by visiting the authorize_uri and manually marking the charge as Successful or Failed.

Receiving the charge completion event

Use webhook events to be notified when a charge is completed. Configure a webhook endpoint on your dashboard to receive the following events:

Event Description
charge.create Fired when the charge is created with status: pending.
charge.complete Fired when the charge is authorized (successfully or not).

Checking the charge status

After receiving the charge.complete event, retrieve the charge using its id and verify that the status in the charge object matches the status in the webhook event.

Status Meaning
successful Payment was received. Fulfill the order.
failed Payment failed. Check failure_code and failure_message for details.
expired The charge was not authorized within the expiry window and cannot be reused.

Failure codes:

Failure Code Description Recommended action
payment_cancelled Payment cancelled by the customer. Ask the customer to retry or select an alternative payment method.
payment_expired Payment expired before authorization was completed. Ask the customer to retry. A new charge must be created.
payment_rejected Payment rejected by the issuer. Ask the customer to check their account or use a different method.
failed_processing General payment processing failure. Ask the customer to retry or select an alternative payment method.
invalid_account No valid account found for the selected payment method. Ask the customer to verify their ShopeePay account and retry.
insufficient_fund Insufficient funds, or the payment method has reached its limit. Ask the customer to top up their account or use a different method.

Testing

You can simulate the full payment flow in test mode without real funds.

  1. Create a source and charge using your test public and secret keys.
  2. Redirect to the authorize_uri returned in the charge response.
  3. On the test authorization page, select Successful or Failed to simulate the customer completing payment in the app.
  4. Verify that your webhook endpoint receives the charge.complete event and that your application updates the order status accordingly.
  5. Confirm that the customer is redirected to your return_uri.

Test mode keys are prefixed with pkey_test_ (public) and skey_test_ (secret). Live mode keys are prefixed with pkey_ and skey_.

Note: In test mode, the authorize_uri simulates the app redirect in a browser. In live mode, the customer is redirected directly into the ShopeePay mobile app. Ensure you test on a real device before going live.


Voids and refunds

You can create a partial or full refund within 180 days of the transaction date via the Refunds API or from the charge page on your dashboard.

Important: Refunds and voids are not available for off-us transactions. When a customer pays using a mobile banking app instead of the ShopeePay wallet directly (an off-us transaction), the payment cannot be refunded or voided through Omise. In these cases, the merchant must handle the refund directly with the customer outside of the Omise system.

All ShopeePay App Redirection payment methods support full refunds within 180 days. Partial refunds are also supported. Voids are not available for any ShopeePay App Redirection payment method — use a refund instead.


Limits

All amounts are in the smallest currency unit. For example, THB 20.00 = 2000, SGD 1.00 = 100, MYR 1.00 = 100.

Thailand (THB)

Limit Amount Amount (THB)
Minimum 2000 THB 20.00
Maximum 15000000 THB 150,000.00

Singapore (SGD)

Limit Amount Amount (SGD)
Minimum 100 SGD 1.00
Maximum 2000000 SGD 20,000.00

Malaysia (MYR)

Limit Amount Amount (MYR)
Minimum 100 MYR 1.00
Maximum 999900 MYR 9,999.00

FAQ

What is the difference between ShopeePay and ShopeePay App Redirection (Jump App)?

Standard ShopeePay uses a redirect flow — the customer is sent to a browser-based page where they can scan a QR code or authorize payment. ShopeePay App Redirection uses an app_redirect flow — the customer is redirected directly into the ShopeePay mobile app. This is optimised for customers already on a mobile device with the ShopeePay app installed.

When should I use platform_type?

If you know the customer's device platform at the time of source creation, pass IOS or ANDROID in the platform_type parameter. This allows Omise to optimise the app redirect for that platform. If omitted, the redirect is handled generically. This parameter is especially useful in native mobile app integrations.

What happens if the customer does not have the ShopeePay app installed?

The redirect to authorize_uri will fail to open the app. The customer will not be able to complete payment. You should consider offering standard ShopeePay (QR-based) as a fallback for desktop users or customers without the app.

What is the default expiry for a ShopeePay App Redirection charge?

20 minutes. This is shorter than standard ShopeePay (60 minutes) to match the app-redirect use case, where the customer is expected to authorize immediately. You can set a custom expiry of up to 60 minutes using the expires_at field at charge creation.

What should my return_uri do when the customer lands on it?

Do not assume the payment was successful just because the customer arrived at return_uri. Always retrieve the charge using its id and check the status field. A customer can land on return_uri after a failed, cancelled, or expired charge as well.

What happens if the customer closes the ShopeePay app before confirming?

The charge remains pending until it expires. You will receive a charge.complete webhook with status: failed and failure_code: payment_expired when this occurs.

Can I set a custom expiry time for a charge?

Yes. Pass a UTC timestamp in the expires_at field at charge creation. The maximum allowed expiry is 60 minutes from charge creation. See Setting the charge to expire for the request format.

What is an off-us transaction and why can't it be refunded?

An off-us transaction occurs when a customer pays using a linked mobile banking app rather than their ShopeePay wallet balance directly. In this case, the payment is processed outside of Omise's settlement chain, so Omise cannot initiate a refund or void. The merchant must handle the refund directly with the customer.

Can I partially refund a ShopeePay App Redirection charge?

Yes. Partial refunds are supported, as long as the refund is initiated within 180 days of the transaction date and the charge was not an off-us transaction.

Is SPayLater available in all supported countries?

Yes. SPayLater is available in Thailand, Singapore, and Malaysia.

What are the payment amount limits?

Limits vary by currency. THB: minimum 2000 (THB 20.00), maximum 15000000 (THB 150,000.00). SGD: minimum 100 (SGD 1.00), maximum 2000000 (SGD 20,000.00). MYR: minimum 100 (MYR 1.00), maximum 999900 (MYR 9,999.00). See Limits for the full breakdown.

Where can I find my API keys?

See How to access Omise API keys.


Omise uses cookies to improve your overall site experience and collect information on your visits and browsing behavior. By continuing to browse our website, you agree to our Privacy Policy. Learn more