Get Started with ShopeePay
Get Started
An overview of the steps to start accepting payments using ShopeePay
- Start by contacting ShopeePay team to sign the NDA (Non-Disclosure Agreement) and commercial agreement.
- Choose your integration to match your business needs:
- In-Person Payments
- Online Payments
- Please refer to the ShopeePay/SPayLater Branding Guideline for Online and In-Person Payment here to showcase the use of ShopeePay/SPayLater during payment flow
- You will be assigned onboarding credentials to start integration testing purposes.
- Develop according to the stated API rules to interact with ShopeePay Payment APIs.
- Follow the Signature Generation steps to create a signature that ShopeePay uses to verify its merchants.
- Visit API Documentations to understand the structure of each API and follow the instructions to send request to the API.
Prerequisite
Merchant should fulfill the following requirements to interact with ShopeePay APIs:
- To be compatible with OAuth 2.0 protocol and HMAC used by ShopeePay to authorize calls.
- Merchants should use TLSv1.2 or TLSv1.3 for TLS handshake.
- Merchants should integrate directly with ShopeePay endpoints without the use of a SDK.
Onboarding
Each merchant will be assigned the following credentials for integration testing purposes.
| Name | Description |
|---|---|
| Client ID | Client refers to the party that integrate with ShopeePay APIs. Each client is assigned a unique identifier known as the ClientID, and this Client ID must be included in the request header of every API call made to ShopeePay’s services. |
| Client Secret | Secret key for signature generation on transaction API. Client secret will be shared offline. |
| Public Key | ShopeePay’s public key which is used by the Client to verify API response/callback. Public key will be shared offline.
|
Merchant will also be required to prepare the following information.
| Name | Description |
|---|---|
| Callback URL | URL to receive payment success notification from ShopeePay.
|
| Private Key | Client's private key which is used by the Client to sign API request. Generate private key in PKIX format: openssl genrsa -out private key.pem 2048
|
| Public Key | Client's public key which is used by ShopeePay to verify API request.
openssl rsa -in privatekey.pem -pubout -out public_key.pem |
API Protocol Rules
This specifies the rules for calling APIs in this document.
| Component | Format/Method |
|---|---|
| Transfer Mode | HTTPS |
| Submit Mode | POST/GET Method |
| Date Format | String - ISO 8601 (yyyy-MM-ddTHH:mm:ssZ , e.g. 2022-07-20T07:15:00+07:00) |
| Char Encoding | UTF-8 |
| Signature | Asymmetric SHA-256withRSA, symmetric HMACSHA-512 |
Request and Response Parameter
Each API documentation includes a mandatory column for both request and response parameters. Here's what the column indicators mean:
- M - Mandatory: These parameters must be included in every request.
- O - Optional: These parameters can be omitted if not needed.
- C - Conditional: These parameters are required only under specific circumstances.
API Parameter Specification
Access Token API
Access Token API Request Header
API header format of Access Token API will require these parameters.
| Field | Type | Mandatory | Description |
|---|---|---|---|
| Content-Type | String | M | Media type of the resource, i.e. application/json |
| X-TIMESTAMP | String | M | Client’s current local time in yyyy-MM-ddTHH:mm:ss.TZD format |
| X-CLIENT-KEY | String | M | ClientID issued by ShopeePay |
| X-SIGNATURE | String | M | X-SIGNATURE: created using asymmetric signature SHA256withRSA algorithm (Private_Key, stringToSign)stringToSign = X-CLIENT-KEY + “|” + X-TIMESTAMP |
Access Token API Response Header
API header format of Access Token API will return these parameters.
| Field | Type | Mandatory | Description |
|---|---|---|---|
| X-TIMESTAMP | String | M | Client’s current local time in yyyy-MM-ddTHH:mm:ss.TZD format |
| X-CLIENT-KEY | String | M | ClientID issued by ShopeePay |
Transactional API
Transaction API Request Header
Each transaction request will require the following parameters in the header
| Field | Type | Mandatory | Description |
|---|---|---|---|
| Content-Type | String | M | Media type of the resource, i.e. application/json |
| X-TIMESTAMP | String | M | Client’s current local time in yyyy-MM-ddTHH:mm:ss.TZD format |
| Authorization | String | C | Represents accessToken of a request, starts with keyword “Bearer” and followed by accessToken |
| X-SIGNATURE | String | M | Refer to Signature Generation and Signature Generation > Transaction Request API for details. |
| X-PARTNER-ID | String | M | Refers to Client ID issued by ShopeePay
|
| X-EXTERNAL-ID | String (36) | M | Numeric string. Reference number that should be unique in the same day under the same ClientID |
| ORIGIN | String | O | Origin domain e.g. www.domain.com |
| CHANNEL-ID | String (5) | M | PJP's channel id. Sample:
|
Signature Generation
A signature is required to authenticate the request. There are 2 types of signature that merchant needs to generate:
- Signature for Access Token
- Signature for Payment API Request
Access Token API
Signature for Access Token API is generated using the asymmetricSHA256withRSA cryptographic function and merchant’s private key
The following steps outline the process for generating signature for AccessToken Request:
- Prepare the data to do signature, the data are:EXAMPLE
-----BEGIN PRIVATE KEY----- MIIEowIBAAKCAQEAycIMIjJ6J7Y9/X1yh19qQTmAmyVg0nYznVNwq8eykY7LjUrAY8PcdJqGZqkv u7cOE/jtuDnvv6g28w8GaaOj+cI60V/CnsbvDhW5C4DrW7/HL8coLx9xuiSSgxZy+khCY7v4IcO4 L0KWP3iJysgoelW7wpTnfEEYtyDbwamOGO9R4ZX4O6nKqyolp00odSHwVB4kI1mjs7LliX/1o/EK 9keWsUKzcKQVBjC3zV1xv036UkLs0Q8iYwpb3FbRlHQs9bG1H5Or6XdsQtwvfUHDWWpXRVu7ttvk //vsHRupocoV5ExJEmFuZMAKBw6cEcawAZIHuObXHhmDPmnlAxWLOwIDAQABAoIBAEPNOlajr/l4 fQybA8oKfqK8uENrJEaWAnJ0gAC6c4AHBNDOtijQwV1OMKx/XtMuiUSc+wZWMgOufAjim70UiR59 71Y9YCILqHqLQkxjXrTOlhmwTAjKAGYVtEbpXGpPrj/mA1UVeOgo8GUUFPpYmYHDHf4eHEzdc3jX SgKjAGXQLhZEXGBBaksR2A9f3eAUGzsVXVJAzUf+yvpj1ACV8iyPt8dz545it79vO9S4miNP2jxC hqr13NrPGPOGboxRLysXTTIUZj582fquzLHD1wcUzcEL1zNINJYmEMKPgAkEWLILSpMPtIINTbdL kUGocP5WKCyAS8LDmHARkPk1WsECgYEA8tV5XAKOUZrvnfDMkW3lrq9qXmopj/rOTzecXxHbrfqb dbUMPOkRFQj9mLNKsPojnOkMrcZPQ0uUTqVsfa8n3/VTyI1jNFK986JNSadzN91ntDSgcbsAX9Tk 6M2xRZ7AxTrD4lplu0xI95ZotnCiQZ3XGvb9HnYq7Upys3j/tTUCgYEA1LJxA5LeKcjDvwcIM1gc +TbIIeFhjYd32RKTuploL0vOq6RjS6KDxXlCbUKQrIvEkeb4pzgrLvY3zEcCexr0wdbpT94kp1oP M7o22BDxILLUXXD1CFfsie4O17Ddkw77KSkRaf4SHXSA7n4d2NRES7f9DsK+kjc8eUUHdF3XfK8C gYAR0ZpTJxjcYhsdItNQBJlrBRIwFWgxWX0UEQeXbk8JaC9KJtvcCFopifxZ3SYo8GH2nJ9CjR+5 12ztjHP2kQjDBVR9jepup3eqzgkP04q/2a5HaekwD0HKxmt5rcZJTonkrxg6ntmCMenUySOr533w hK2JHACc4Jzrxp++Da3t1QKBgGMw0FmNTYQI95iHjAB90A08yfpa5CafjXmzGyfDUP31iW0sXY4x POiD00Gm8Fc3WzV7lGxPWnwtIPpoBzUn7grT0byIaWmOK1bBOcBrrjfEjhsBiZQZhNsSJOPbvIlP TDv2xgM7FHGeGl6efAbZfvwc0qvmj/8aOV6InaBb/xlLAoGBAITXH4/7guOJWflLQhDYzuzCHFE2 q+n3hJnS6TyuErezwDoDCFsXH4CJg+N/vzfduFu2fGXC8pqiXdyEX1BL6HhY1HQ3q5btD8iTRY7o Ixc61T3hLKSl4njHS6+Ern1+aAJ9mNFnSPxTCs1swuKtnSQZlFNUoXa/Miw62Fek5Prb -----END PRIVATE KEY-----Data Description Example X-CLIENT-KEY ClientID issued by ShopeePay sample-client-id X-TIMESTAMP Client’s current local time in yyyy-MM-ddTHH:mm:ss.TZD format 2022-08-11T11:13:43+08:00 - Generate the stringToSign (X-SIGNATURE) using:EXAMPLE
<X-CLIENT-KEY> + ”|” + <X-TIMESTAMP>Sample:
sample-client-id|2022-08-11T11:13:43+08:00 - Generate the signature by using merchant’s private key to encrypt the value using SHA256withRSA function. Sample codes in multiple programming languages are listed below step 4:EXAMPLE
SYaKdVb4sB3Lq19TA7yDr/aH15xjteF2k1bbtWBAEOA/gIxKuSscCpZu5H5IKhPWnIweQtMBKgtUWdVncdOHOCVNRQ+SzUvHHQ7GHJTPtsedkbd3OnBHwCeh3tLPIQ5MOtHe/0WVcYwIPnZ8dj5Xa3afoUdfmFPEiK4Fb2nbc+5B9zaC+PkRtK6en1Hux4AqS9TUgdr7dR6Vat1vofe7hM7gzotjN0bHaCoH34S4WAJfPl79paZksAvl7I/E36ShgLGy2E/SOv9zChW/sOQxPLmJP8TgnM2cuKzDZBHD8Gn7ne5DGov13/N3++K/r0kU1i9kwTEZHzareSE+ObIOkQ== - Put the signature into Access Token HTTP header: "X-SIGNATURE"EXAMPLE
X-SIGNATURE: SYaKdVb4sB3Lq19TA7yDr/aH15xjteF2k1bbtWBAEOA/gIxKuSscCpZu5H5IKhPWnIweQtMBKgtUWdVncdOHOCVNRQ+SzUvHHQ7GHJTPtsedkbd3OnBHwCeh3tLPIQ5MOtHe/0WVcYwIPnZ8dj5Xa3afoUdfmFPEiK4Fb2nbc+5B9zaC+PkRtK6en1Hux4AqS9TUgdr7dR6Vat1vofe7hM7gzotjN0bHaCoH34S4WAJfPl79paZksAvl7I/E36ShgLGy2E/SOv9zChW/sOQxPLmJP8TgnM2cuKzDZBHD8Gn7ne5DGov13/N3++K/r0kU1i9kwTEZHzareSE+ObIOkQ==
Python
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
import base64
def sha256_with_rsa(private_key, payload):
key = RSA.import_key(private_key)
h = SHA256.new(payload)
signature = pkcs1_15.new(key).sign(h)
byte_values = bytes.fromhex(signature.hex())
result_string = base64.b64encode(byte_values)
return result_string.decode('utf-8')Transactional API
Signature for Transactional API is generated using the symmetric HMACSHA-512 cryptographic function using an access token.
The following steps outline the process for generating signature for Payment API Request:
- Prepare the data to do signature, the data are:
Data Description Example HTTP Method Method on each API for instance: GET, POST POST EndpointURL - EndpointURL is Relative path along with query parameters
- EndpointURL must be escaped. Please refer here for the escaped function
/v1.0/qr/qr-mpm-generate AccessToken An authorization issued to the client that used to access protected resources AccessToken obtained by calling Access Token endpoint HTTP Body Raw body of the HTTP { "partnerReferenceNo": "F03B527114E9G0C7FG494", "amount": { "value": "17.00", "currency": "IDR" }, "feeAmount": { "value": "0.00", "currency": "IDR" }, "merchantId": "1209121278", "terminalId": "", "validityPeriod": "2022-07-22T10:45:58+08:00", "additionalInfo": { "externalStoreId": "2233", "convenienceFeeIndicator": "01", "promoIds": "" }} X-TIMESTAMP Client’s current local time in yyyy-MM-ddTHH:mm:ss.SSSTZD format 2022-08-02T19:16:20+08:00 - Minify the request body
Before Minify:
JSON REQUEST{ "partnerReferenceNo": "F03B527114E9G0C7FG494", "amount": { "value": "17.00", "currency": "IDR" }, "feeAmount": { "value": "0.00", "currency": "IDR" }, "merchantId": "1209121278", "terminalId": "", "validityPeriod": "2022-07-22T10:45:58+08:00", "additionalInfo": { "externalStoreId": "2233", "convenienceFeeIndicator": "01", "promoIds": "" } }After Minify:
JSON REQUEST{"partnerReferenceNo":"F03B527114E9G0C7FG494","amount":{"value":"17.00","currency":"IDR"},"feeAmount":{"value":"0.00","currency":"IDR"},"merchantId":"1209121278","terminalId":"","validityPeriod":"2022-07-22T10:45:58+08:00","additionalInfo":{"externalStoreId":"2233","convenienceFeeIndicator":"01","promoIds":""}} - Generate body hash: Lowercase(HexEncode(SHA-256(RequestBody))). The hex encoded SHA-256 hash of the request body should look like this:EXAMPLE
3fb04055423bc3a488c923b134d1c9ca9de68132aef7535d3b97f614a165df8c - Generate the final payload by composing the stringToSigEXAMPLE
HTTP METHOD + “:” + EndpointURL + “:” + AccessToken + “:” Lowercase(HexEncode(SHA-256(RequestBody))) “:” + <X-TIMESTAMP>This is how the stringToSign look like:
EXAMPLEPOST:/v1.0/qr/qr-mpm-generate:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJDbGllbnRJRCI6Ik1IMjAyMjA3MDhCIiwiZXhwIjoxNjU4NzIwOTk5NjY5LCJpYXQiOjE2NTg3MTczOTk2NjksImlzcyI6Ik1IMjAyMjA3MDhCIiwibmJmIjoxNjU4NzE3Mzk5NjY5fQ.hv0dubKQTeTR4n2VK\_qzf8N3MPUv1wEc8LiMa1eglVc:3fb04055423bc3a488c923b134d1c9ca9de68132aef7535d3b97f614a165df8c:2022-08-02T19:16:20+08:00</u>](http://post/v1.0/qr/qr-mpm-generate:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJDbGllbnRJRCI6Ik1IMjAyMjA3MDhCIiwiZXhwIjoxNjU4NzIwOTk5NjY5LCJpYXQiOjE2NTg3MTczOTk2NjksImlzcyI6Ik1IMjAyMjA3MDhCIiwibmJmIjoxNjU4NzE3Mzk5NjY5fQ.hv0dubKQTeTR4n2VK_qzf8N3MPUv1wEc8LiMa1eglVc:3fb04055423bc3a488c923b134d1c9ca9de68132aef7535d3b97f614a165df8c:2022-08-02T19:16:20+08:00 - Generate signature using the final payload. The symmetric HMAC_SHA512 (clientSecret, stringToSign) should look like this. Sample codes in multiple programming languages are listed below step 6:EXAMPLE
3ZcDuB8ZBJBqN+jWdoH1nmGg+z+n1hkE6JOGQ3ziWTgX+vU4+CdypOFwBNbT1SZ7zRryIFqQr5/eLHIFGO//EA== - Put the signature into Transactional API Request HTTP header: "X-SIGNATURE"EXAMPLE
X-SIGNATURE: 3ZcDuB8ZBJBqN+jWdoH1nmGg+z+n1hkE6JOGQ3ziWTgX+vU4+CdypOFwBNbT1SZ7zRryIFqQr5/eLHIFGO//EA==
Note:
- EndpointURL consists of a relative path including the Request Parameters. For example: /1.0/get-auth-code/test?uid=1234&order_id=1234
- EndpointURL must be escaped. For example, an unescaped url looks like this/1.0/get-auth-code/test?uid=1234&order_id=1234. You need to escape the whole Request Parameters to /1.0/get-auth-code/test?uid%3D1234%26order_id%3D1234. You can refer here for the escape function. We suggest you to unescape first before escaping the EndpointURL. Below is a javascript exampleEXAMPLE
escapedParameters = encodeURIComponent(unescape(pm.request.url.query)) - If does not have minify(Request Body), then can use empty string.
Python
import json
import hashlib
import hmac
import base64
def minify(body: bytes) -> bytes:
if not body:
raise ValueError("body is empty")
try:
# Parse and compact JSON
parsed_json = json.loads(body.decode('utf-8'))
minified_json = json.dumps(parsed_json, separators=(',', ':'))
return minified_json.encode('utf-8')
except json.JSONDecodeError as e:
raise ValueError(f"Failure encountered compacting JSON: {e}")
def body_sha256_with_hex_encode(body: bytes) -> str:
sha256_hash = hashlib.sha256(body).hexdigest()
return sha256_hash.lower()
def gen_payload(method: str, url: str, token: str, body_hash: str, timestamp: str) -> bytes:
payload = f"{method}:{url}:{token}:{body_hash}:{timestamp}"
return payload.encode('utf-8')
def hmac_sha512(body: bytes, token: str, timestamp: str, method: str, url: str, client_secret: str) -> str:
try:
# Minify the body
mini_body = minify(body)
except ValueError as e:
return "", str(e)
# Generate body hash
body_hash = body_sha256_with_hex_encode(mini_body)
# Generate final payload
payload = gen_payload(method, url, token, body_hash, timestamp)
# Use payload to generate signature
hmac_sha512 = hmac.new(client_secret.encode('utf-8'), payload, hashlib.sha512)
final_sign = base64.b64encode(hmac_sha512.digest()).decode('utf-8')
return final_sign, None
Callback Signature Validation
Upon receiving the API callback, Client should verify the signature of the callback in the following ways.
- Take the signature from HTTP header "X-SIGNATURE" in the callback request from ShopeePay
Example signature:
EXAMPLEhnha4pl/3qPxBlAoRLvxzQzCt3wWqBHDTQfRkvI9o7ZDkwsM9NI0YxZVkb/TyDu7wrkv7EVubMihyt4Kn1cjpXoU9MRQ2MHFAsN9tm3R7Qj0kQwvmhCsjOSp9zPhBfT99Syg4TdjHpLPMnxPHEmarZKHPzCdcJCtdz1ohZ6ilamyjLazICXpsezf4V3bZSiXMTS1CB7r3Nc0FFq6x2dMTCV0FU0/rJT7TVtntpgHee2xtcJIPpAqb+zyEAeyBaC5oGtTqlgd5D/bmAbi+t8wPdMTWC0xVpzoHdALOPoStwaxooMiGBsuGYz5xEgCFYGPOYN5dkPS/QJHvd+DhxhbGA== - Generate stringToSign based on this following format:
Format:
EXAMPLEHTTPMethod + ":" + callbackURL + ":" + Hex(SHA256(requestBody)) + ":" + timestampNote:
- The SHA256 sum needs to be formatted using hexadecimal integer.
- The value of timestamp is taken from the HTTP header "X-TIMESTAMP" in the callback request.
- ShopeePay uses full path including the domain for merhant’s callbackURL
Example:
EXAMPLEPOST:https://example.callback.com/Shopee/v1.0/debit/notify:2ae8474e00b1a65fc67cba1b4f6446b94bb50a0fc9f575ae10d545a2ddc98068:2024-03-04T08:44:30+07:00 - Verify the correctness of the signature based on Asymmetric SHA-256withRSA encryption using ShopeePay public key.
Sample Codes to verify the signature:
Python
import base64
import hashlib
import hmac
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
# Replace the Shopee public key with your actual key
shopee_pubkey = """-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----"""
def verify_signature(callback_url, req_payload, timestamp, sign_to_verify):
# Compute string to sign
string_to_sign = ':'.join([
'POST',
callback_url,
hashlib.sha256(req_payload.encode('utf-8')).hexdigest(),
timestamp
])
print('string to sign:', string_to_sign)
# Compute hash of string to sign
h = SHA256.new(bytes(string_to_sign, 'utf-8'))
# Load ShopeePay public key
pub_key = RSA.import_key(shopee_pubkey)
# Base64 decode the signature
signature_bytes = base64.b64decode(sign_to_verify)
try:
pkcs1_15.new(pub_key).verify(h, signature_bytes)
return True
except (ValueError, TypeError):
return False
# Example usage
callback_url = 'https://example.com/Shopee/v1.0/debit/notify'
req_payload = '{"example":"request_payload"}'
timestamp = '2024-03-04T08:44:30+07:00'
sign_to_verify = 'signature to be verified'
print(verify_signature(callback_url, req_payload, timestamp, sign_to_verify))
Response Body
The ShopeePay API response body is in JSON format.
- Parameters can be returned in random sequence.
- Additional parameters may be returned in future without advance notice from ShopeePay.
- Empty parameters may be returned in the following format by default, unless stated otherwise.
| Type | Empty Response |
|---|---|
| Integer | 0 |
| String | Empty string, or "0" if the field represents a numerical value )e.g: amount, timestamp) |
| Object | Null |
| Array | Empty array |
| Boolean | False |
Client system’s logic should not assume that the order of arrangement of response parameters or the total number of parameters returned will remain constant throughout time.
Access Nodes
ShopeePay access nodes are country specific, each country will have their own API domains to be called.
Region Domain Suffix
| Region | Abbreviation | Code |
|---|---|---|
| Indonesia | ID | co.id |
Append the region code to the end of the domain.
| Environment | Domain |
|---|---|
| sandbox | api.snap.uat.airpay.co.id |
| live | api.snap.airpay.co.id |
Backward Compatible Changes
Backward compatible or non-breaking changes refer to API changes that allow the integration to continue using the API without any additional changes required at Merchant’s side. ShopeePay may make such changes on their sole discretion without informing the existing Merchants in advance, as such changes are deemed to not break any integration implemented by the Merchant.
ShopeePay considers the following changes as non-breaking:
- Add new API resource or new types of server callback to a new endpoint.
- Add new or remove optional field to existing API Request Parameters.
- Add new fields to existing API response/server callback parameters.
- Increase or decrease the max length limit of existing fields on existing API response / server callback parameters.
- Change the order of fields in existing API responses or server callback parameters.
- Increase the max length limit of request fields on existing API Request Parameters.
IMPORTANT:
ShopeePay may introduce new parameters in future updates. Merchants should avoid enforcing strict validation on the response body and callback parameters to ensure seamless compatibility.