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:

❶ 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:
- Create a payment source (
type:shopeepay_jumpapp) using Omise.js or one of the mobile SDKs (iOS or Android). - Create a charge using the source identifier from step 1.
- 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
typeparameter is the first argument passed to thecreateSourcemethod.
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.amountandcurrency— must match the values used when creating the source.
Omise recommends not using the charge attributes
net,fee,fee_vat, andtransaction_feesuntil the chargestatusissuccessful.
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:
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.
- Create a source and charge using your test public and secret keys.
- Redirect to the
authorize_urireturned in the charge response. - On the test authorization page, select Successful or Failed to simulate the customer completing payment in the app.
- Verify that your webhook endpoint receives the
charge.completeevent and that your application updates the order status accordingly. - Confirm that the customer is redirected to your
return_uri.
Test mode keys are prefixed with
pkey_test_(public) andskey_test_(secret). Live mode keys are prefixed withpkey_andskey_.Note: In test mode, the
authorize_urisimulates 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.