Skip to main content

ShopeePay Jump App

Accept payments with app-to-app redirection to the ShopeePay mobile app for a seamless mobile checkout experience.

Overviewโ€‹

ShopeePay Jump App is an app-to-app redirection payment method that deep links customers directly from your mobile app or website to the ShopeePay mobile app for payment authorization. This provides a faster, more native mobile experience compared to QR code scanning.

Key Features:

  • โœ… App-to-app redirection - Direct deep link to ShopeePay app
  • โœ… Multiple payment options - Wallet, cards, bank accounts, SPayLater
  • โœ… 3 markets - Thailand, Singapore, Malaysia
  • โœ… Fast checkout - Native app experience
  • โœ… Platform detection - iOS and Android support
  • โœ… 180-day refund window - Full and partial refunds
Enablement Required

Contact support@omise.co to enable ShopeePay Jump App for your merchant account.

Supported Regionsโ€‹

RegionCurrencyMin AmountMax AmountAPI Version
ThailandTHBเธฟ20.00เธฟ150,000.002017-11-02
SingaporeSGD$1.00$20,000.002017-11-02
MalaysiaMYRRM1.00RM9,999.002017-11-02

Payment Options by Regionโ€‹

Payment OptionThailandSingaporeMalaysia
Wallet Balanceโœ…โœ…โœ…
Credit Cardโœ…โœ…โŒ
Direct Debit / Bankโœ…โŒโŒ
SPayLater (BNPL)โœ…โœ…โŒ

How It Worksโ€‹

Payment Flow:

  1. Customer selects ShopeePay on mobile device
  2. Merchant creates source with shopeepay_jumpapp type
  3. Customer is redirected via deep link
  4. ShopeePay app opens automatically
  5. Customer selects payment method (wallet, card, SPayLater)
  6. Customer authenticates and confirms
  7. Customer returns to merchant app/site
  8. Webhook confirms payment status

Typical completion time: 30 seconds - 2 minutes

Implementationโ€‹

Step 1: Create Sourceโ€‹

curl https://api.omise.co/sources \
-u $OMISE_PUBLIC_KEY: \
-d "amount=50000" \
-d "currency=THB" \
-d "type=shopeepay_jumpapp" \
-d "platform_type=ANDROID"

Step 2: Create Chargeโ€‹

curl https://api.omise.co/charges \
-u $OMISE_SECRET_KEY: \
-d "amount=50000" \
-d "currency=THB" \
-d "return_uri=https://example.com/payment/complete" \
-d "source=src_test_xxx"

Combined Requestโ€‹

Create source and charge in one request:

curl https://api.omise.co/charges \
-u $OMISE_SECRET_KEY: \
-d "amount=50000" \
-d "currency=THB" \
-d "return_uri=https://example.com/payment/complete" \
-d "source[type]=shopeepay_jumpapp" \
-d "source[platform_type]=ANDROID"

Step 3: Redirect Customerโ€‹

// Detect platform and redirect
function redirectToShopeePay(authorizeUri) {
// The authorize_uri contains the deep link
window.location.href = authorizeUri;
}

// After creating charge
redirectToShopeePay(charge.authorize_uri);

Step 4: Handle Returnโ€‹

app.get('/payment/complete', async (req, res) => {
try {
// Get charge status
const chargeId = req.query.charge;
const charge = await omise.charges.retrieve(chargeId);

if (charge.status === 'successful') {
await fulfillOrder(charge.metadata.order_id);
res.redirect('/order-success');
} else if (charge.status === 'failed') {
res.redirect('/payment-failed?error=' + charge.failure_code);
} else {
// Still pending - wait for webhook
res.redirect('/payment-pending');
}
} catch (error) {
res.redirect('/payment-error');
}
});

Step 5: Handle Webhookโ€‹

app.post('/webhooks/omise', (req, res) => {
const event = req.body;

if (event.key === 'charge.complete') {
const charge = event.data;

if (charge.source.type === 'shopeepay_jumpapp') {
if (charge.status === 'successful') {
fulfillOrder(charge.metadata.order_id);
} else if (charge.status === 'failed') {
handleFailedPayment(charge);
}
}
}

res.status(200).send('OK');
});

Complete Implementation Exampleโ€‹

const express = require('express');
const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});

const app = express();
app.use(express.json());

// Checkout endpoint
app.post('/checkout/shopeepay-jumpapp', async (req, res) => {
try {
const { amount, currency, order_id, platform_type } = req.body;

// Validate currency
if (!['THB', 'SGD', 'MYR'].includes(currency)) {
return res.status(400).json({
error: 'Currency not supported. Use THB, SGD, or MYR.'
});
}

// Validate platform
const platform = ['IOS', 'ANDROID'].includes(platform_type)
? platform_type
: 'ANDROID';

// Create source
const source = await omise.sources.create({
type: 'shopeepay_jumpapp',
amount: amount,
currency: currency,
platform_type: platform
});

// Create charge with 20-minute expiry
const charge = await omise.charges.create({
amount: amount,
currency: currency,
source: source.id,
return_uri: `${process.env.BASE_URL}/payment/complete`,
metadata: {
order_id: order_id
}
});

// Return deep link URL
res.json({
authorize_uri: charge.authorize_uri,
charge_id: charge.id,
expires_at: charge.expires_at
});

} catch (error) {
console.error('ShopeePay Jump App error:', error);
res.status(500).json({ error: error.message });
}
});

// Return handler
app.get('/payment/complete', async (req, res) => {
const chargeId = req.query.charge;

try {
const charge = await omise.charges.retrieve(chargeId);

if (charge.status === 'successful') {
res.redirect(`/order-confirmation?order=${charge.metadata.order_id}`);
} else {
res.redirect(`/payment-failed?charge=${chargeId}`);
}
} catch (error) {
res.redirect('/payment-error');
}
});

// Webhook handler
app.post('/webhooks/omise', (req, res) => {
const event = req.body;

if (event.key === 'charge.complete') {
const charge = event.data;

if (charge.source.type === 'shopeepay_jumpapp') {
if (charge.status === 'successful') {
processOrder(charge.metadata.order_id);
sendConfirmationEmail(charge);
} else {
notifyPaymentFailure(charge);
}
}
}

res.sendStatus(200);
});

app.listen(3000);

Mobile App Integrationโ€‹

iOS (Swift)โ€‹

func openShopeePayJumpApp(authorizeUri: String) {
guard let url = URL(string: authorizeUri) else { return }

UIApplication.shared.open(url, options: [:]) { success in
if !success {
// ShopeePay app not installed - show fallback
self.showAppStorePrompt()
}
}
}

Android (Kotlin)โ€‹

fun openShopeePayJumpApp(authorizeUri: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(authorizeUri))

try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
// ShopeePay app not installed - show fallback
showPlayStorePrompt()
}
}

React Nativeโ€‹

import { Linking } from 'react-native';

const openShopeePayJumpApp = async (authorizeUri) => {
try {
const supported = await Linking.canOpenURL(authorizeUri);

if (supported) {
await Linking.openURL(authorizeUri);
} else {
// Fallback to app store
showAppStorePrompt();
}
} catch (error) {
console.error('Failed to open ShopeePay:', error);
}
};

Charge Expirationโ€‹

Default charge expiration is 20 minutes. You can customize this up to 60 minutes:

curl https://api.omise.co/charges \
-u $OMISE_SECRET_KEY: \
-d "amount=50000" \
-d "currency=THB" \
-d "source=src_test_xxx" \
-d "return_uri=https://example.com/complete" \
-d "expires_at=2024-12-31T23:59:59Z"

Charge Status Valuesโ€‹

StatusDescription
pendingAwaiting customer authorization in ShopeePay app
successfulPayment completed
failedPayment declined or error
expiredCustomer didn't complete within expiry window

Failure Codesโ€‹

CodeDescription
payment_expiredAuthorization window expired
payment_rejectedShopeePay declined transaction
insufficient_balanceInsufficient wallet balance
failed_processingGeneral processing error

Refundsโ€‹

ShopeePay Jump App supports full and partial refunds within 180 days:

// Full or partial refund
const refund = await omise.charges.refund('chrg_test_xxx', {
amount: 25000 // Partial refund of THB 250.00
});
Off-Us Transactions

Refunds and voids are not available for "off-us" transactions (payments made with cards or bank accounts not directly through ShopeePay wallet).

Best Practicesโ€‹

  1. Detect platform - Always detect and pass the correct platform_type (IOS/ANDROID)
  2. Handle app not installed - Provide fallback if ShopeePay app isn't installed
  3. Mobile-only - This method requires a mobile device with ShopeePay app
  4. Set appropriate expiry - Match expiration to your checkout session
  5. Webhook priority - Always use webhooks for order fulfillment

FAQโ€‹

What is the difference between ShopeePay Jump App and ShopeePay QR?

ShopeePay Jump App (shopeepay_jumpapp): App-to-app redirection that deep links directly to the ShopeePay mobile app. Better for mobile app integrations.

ShopeePay QR (shopeepay): Displays a QR code that customers scan with their ShopeePay app. Works on both desktop and mobile.

What payment methods can customers use?

Customers can pay using:

  • ShopeePay Wallet Balance - All regions
  • Credit/Debit Cards - Thailand, Singapore
  • Direct Debit / Bank Account - Thailand only
  • SPayLater (BNPL) - Thailand, Singapore

The available options depend on the customer's region and account settings.

What is SPayLater?

SPayLater is Shopee's Buy Now Pay Later service that allows customers to pay in installments. It's available in Thailand and Singapore. Customers apply for SPayLater credit through the Shopee app.

Do I need to specify the platform type?

The platform_type parameter (IOS or ANDROID) is optional but recommended. It helps optimize the deep link for the customer's device. If not specified, the system will attempt to detect it automatically.

What if the customer doesn't have the ShopeePay app installed?

If the ShopeePay app isn't installed, the deep link won't work. Implement a fallback that:

  1. Detects if the app opens successfully
  2. Redirects to app store if not
  3. Offers alternative payment methods
How long does the customer have to complete payment?

By default, customers have 20 minutes to complete the payment. You can extend this to up to 60 minutes using the expires_at parameter when creating the charge.

Can I use this on desktop?

ShopeePay Jump App is designed for mobile devices. For desktop users, use ShopeePay QR instead, which displays a QR code they can scan with their phone.