Get Started with ShopeePay

Get Started

An overview of the steps to start accepting payments using ShopeePay

  1. Start by contacting ShopeePay team to sign the NDA (Non-Disclosure Agreement) and commercial agreement.
  2. 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
  3. You will be assigned onboarding credentials to start integration testing purposes.
  4. Develop according to the stated API rules to interact with ShopeePay Payment APIs.
  5. Follow the Signature Generation steps to create a signature that ShopeePay uses to verify its merchants.
  6. 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.

NameDescription
Client IDClient 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 SecretSecret key for signature generation on transaction API. Client secret will be shared offline.
Public KeyShopeePay’s public key which is used by the Client to verify API response/callback. Public key will be shared offline.
  • ShopeePay Public Key will be provided in PKIX format

Merchant will also be required to prepare the following information.

NameDescription
Callback URLURL to receive payment success notification from ShopeePay.
  • Callback URL will be shared offline
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

  • Replace 2048 with your desired key length.

Public Key

Client's public key which is used by ShopeePay to verify API request.

  • Public key needs to be shared by client offline.
  • Public key should be using PKIX format.
Generate public key in PKIX format: openssl rsa -in privatekey.pem -pubout -out public_key.pem

API Protocol Rules

This specifies the rules for calling APIs in this document.

ComponentFormat/Method
Transfer ModeHTTPS
Submit ModePOST/GET Method
Date FormatString - ISO 8601 (yyyy-MM-ddTHH:mm:ssZ , e.g. 2022-07-20T07:15:00+07:00)
Char EncodingUTF-8
SignatureAsymmetric 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.

FieldTypeMandatoryDescription
Content-TypeStringMMedia type of the resource, i.e. application/json
X-TIMESTAMPStringMClient’s current local time in yyyy-MM-ddTHH:mm:ss.TZD format
X-CLIENT-KEYStringMClientID issued by ShopeePay
X-SIGNATUREStringMX-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.

FieldTypeMandatoryDescription
X-TIMESTAMPStringMClient’s current local time in yyyy-MM-ddTHH:mm:ss.TZD format
X-CLIENT-KEYStringMClientID issued by ShopeePay

Transactional API

Transaction API Request Header

Each transaction request will require the following parameters in the header

FieldTypeMandatoryDescription
Content-TypeStringMMedia type of the resource, i.e. application/json
X-TIMESTAMPStringMClient’s current local time in yyyy-MM-ddTHH:mm:ss.TZD format
AuthorizationStringCRepresents accessToken of a request, starts with keyword “Bearer” and followed by accessToken
X-SIGNATUREStringMRefer to Signature Generation and Signature Generation > Transaction Request API for details.
X-PARTNER-IDStringMRefers to Client ID issued by ShopeePay
  • Unique identifier for caller
X-EXTERNAL-IDString (36)MNumeric string. Reference number that should be unique in the same day under the same ClientID
ORIGINStringOOrigin domain e.g. www.domain.com
CHANNEL-IDString (5)M

PJP's channel id. Sample:

  • App
  • PC
  • mweb
Device identification on which the API services is currently being accessed by the end user (customer)

Signature Generation

A signature is required to authenticate the request. There are 2 types of signature that merchant needs to generate:

  1. Signature for Access Token
  2. 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:

1. 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-----
arrow-svg

Copy

DataDescriptionExample
X-CLIENT-KEYClientID issued by ShopeePaysample-client-id
X-TIMESTAMPClient’s current local time in yyyy-MM-ddTHH:mm:ss.TZD format2022-08-11T11:13:43+08:00

2. Generate the stringToSign (X-SIGNATURE) using:

EXAMPLE

<X-CLIENT-KEY> + ”|” + <X-TIMESTAMP>
arrow-svg

Copy

Sample: sample-client-id|2022-08-11T11:13:43+08:00

3. 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==
arrow-svg

Copy

4. 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==
arrow-svg

Copy

Go

GO EXAMPLE

import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
)
// OpenAPISha256WithRSA - sign the `payload` with `private key`
func OpenAPISha256WithRSA(key *rsa.PrivateKey, payload []byte) (string, error) {
h := sha256.New()
h.Write(payload)
hash := h.Sum(nil)
signature, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, hash)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(signature), nil
}
arrow-svg

Copy

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:

1. Prepare the data to do signature, the data are:

DataDescriptionExample
HTTP MethodMethod on each API for instance: GET, POSTPOST
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
AccessTokenAn authorization issued to the client that used to access protected resourcesAccessToken obtained by calling Access Token endpoint
HTTP BODYRaw 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-TIMESTAMP2022-08-02T19:16:20+08:00

2. Minify the request body

Before Minify:

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": ""
}
}
arrow-svg

Copy

After Minify:

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":""}}
arrow-svg

Copy

3. Generate body hash: Lowercase(HexEncode(SHA-256(RequestBody))). The hex encoded SHA-256 hash of the request body  should look like this:

EXAMPLE

3fb04055423bc3a488c923b134d1c9ca9de68132aef7535d3b97f614a165df8c
arrow-svg

Copy

4. Generate the final payload by composing the stringToSig

EXAMPLE

HTTP METHOD + ":" + EndpointURL + ":" + AccessToken + ":" Lowercase(HexEncode(SHA-256(RequestBody))) ":" + <X-TIMESTAMP>
arrow-svg

Copy

This is how the stringToSign look like:

EXAMPLE

POST:/v1.0/qr/qr-mpm-generate:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJDbGllbnRJRCI6Ik1IMjAyMjA3MDhCIiwiZXhwIjoxNjU4NzIwOTk5NjY5LCJpYXQiOjE2NTg3MTczOTk2NjksImlzcyI6Ik1IMjAyMjA3MDhCIiwibmJmIjoxNjU4NzE3Mzk5NjY5fQ.hv0dubKQTeTR4n2VK_qzf8N3MPUv1wEc8LiMa1eglVc:3fb04055423bc3a488c923b134d1c9ca9de68132aef7535d3b97f614a165df8c:2022-08-02T19:16:20+08:00
arrow-svg

Copy

5. 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==
arrow-svg

Copy

6. Put the signature into Transactional API Request HTTP header: "X-SIGNATURE"

EXAMPLE

X-SIGNATURE: 3ZcDuB8ZBJBqN+jWdoH1nmGg+z+n1hkE6JOGQ3ziWTgX+vU4+CdypOFwBNbT1SZ7zRryIFqQr5/eLHIFGO//EA==
arrow-svg

Copy

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 example

EXAMPLE

escapedParameters = encodeURIComponent(unescape(pm.request.url.query))
arrow-svg

Copy

  • If does not have minify(Request Body), then can use empty string.

Go

GO EXAMPLE

import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"strings"
)
func HmacSha512(body []byte, token string, timestamp string, method string, url string, clientSecret string) (string, error) {
//1. firstly, minify your request body, remove `\n`, whitespace and so on
miniBody, err := Minify(body)
if err != nil {
return "", err
}
//2. generate body hash, hash = hex.Encode(sha256(body))
bodyHash := BodySha256WithHexEncode(miniBody)
//3. generate final payload
//noted: url will contain Request Parameters, like /base/v1/openapi_test?uid=4
//noted: if body is empty, will use an empty string to generate
payload := GenPayload(method, url, token, bodyHash, timestamp)
//4. use payload to generate signature
hmacsha512Hash := hmac.New(sha512.New, []byte(clientSecret))
hmacsha512Hash.Write(payload)
finalSign := base64.StdEncoding.EncodeToString(hmacsha512Hash.Sum(nil))
fmt.Println("finalSign", finalSign)
fmt.Println(payload)
return finalSign, errors.New("body param error")
}
func BodySha256WithHexEncode(body []byte) string {
hash := sha256.New()
hash.Write(body)
return strings.ToLower(hex.EncodeToString(hash.Sum(nil)))
}
func Minify(body []byte) ([]byte, error) {
if len(body) == 0 {
return []byte{}, errors.New("body is empty")
}
buff := new(bytes.Buffer)
errCompact := json.Compact(buff, body)
if errCompact != nil {
newErr := fmt.Errorf("failure encountered compacting json := %v", errCompact)
return nil, newErr
}
b, err := ioutil.ReadAll(buff)
if err != nil {
readErr := fmt.Errorf("read buffer error encountered := %v", err)
return nil, readErr
}
return b, nil
}
func GenPayload(method string, url string, token string, bodyHash string, timestamp string) []byte {
return []byte(fmt.Sprintf("%s:%s:%s:%s:%s", method, url, token, bodyHash, timestamp))
}
arrow-svg

Copy

Callback Signature Validation

Upon receiving the API callback, Client should verify the signature of the callback in the following ways.

1. Take the signature from HTTP header "X-SIGNATURE" in the callback request from ShopeePay

Example signature:

JS EXAMPLE

hnha4pl/3qPxBlAoRLvxzQzCt3wWqBHDTQfRkvI9o7ZDkwsM9NI0YxZVkb/TyDu7wrkv7EVubMihyt4Kn1cjpXoU9MRQ2MHFAsN9tm3R7Qj0kQwvmhCsjOSp9zPhBfT99Syg4TdjHpLPMnxPHEmarZKHPzCdcJCtdz1ohZ6ilamyjLazICXpsezf4V3bZSiXMTS1CB7r3Nc0FFq6x2dMTCV0FU0/rJT7TVtntpgHee2xtcJIPpAqb+zyEAeyBaC5oGtTqlgd5D/bmAbi+t8wPdMTWC0xVpzoHdALOPoStwaxooMiGBsuGYz5xEgCFYGPOYN5dkPS/QJHvd+DhxhbGA==
arrow-svg

Copy

2. Generate stringToSign based on this following format:

Format:

JS EXAMPLE

HTTPMethod + ":" + callbackURL + ":" + Hex(SHA256(requestBody)) + ":" + timestamp
arrow-svg

Copy

Note:

  • 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:

JS EXAMPLE

POST:https://example.callback.com/Shopee/v1.0/debit/notify:2ae8474e00b1a65fc67cba1b4f6446b94bb50a0fc9f575ae10d545a2ddc98068:2024-03-04T08:44:30+07:00
arrow-svg

Copy

3. Verify the correctness of the signature based on Asymmetric SHA-256withRSA encryption using ShopeePay public key. 

Sample Codes to verify the signature:

Go

GO EXAMPLE

package main
import (
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"net/http"
"strings"
)
var shopeePubkey = `-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----`
// callbackURL - callback url of merchant
// reqPayload - the HTTP request payload
// timestamp - the value of HTTP request header X-TIMESTAMP
// signToVerify - the value of HTTP request header X-SIGNATURE
func verifySignature(callbackURL string, reqPayload string, timestamp string, signToVerify string) error {
//compute string to sign
stringToSign := strings.Join([]string{
http.MethodPost,
callbackURL,
fmt.Sprintf("%x", sha256.Sum256([]byte(reqPayload))),
timestamp,
}, ":")
fmt.Println("string to sign:", stringToSign)
//compute hash of string to sign
hashedMsg := sha256.Sum256([]byte(stringToSign))
//load shopeepay public key
block, _ := pem.Decode([]byte(shopeePubkey))
var pubKey *rsa.PublicKey
pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes)
pubKey = pubInterface.(*rsa.PublicKey)
//base64 decode the signature
signatureByte, _ := base64.StdEncoding.DecodeString(signToVerify)
//verify the signature by shopeepay public key
return rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, hashedMsg[:], signatureByte)
}
func main() {
fmt.Println(verifySignature(merchantCallback, body, headers["X-TIMESTAMP"], headers["X-SIGNATURE"]))
}
arrow-svg

Copy

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. 
TypeEmpty Response
Integer0
StringEmpty string, or "0" if the field represents a numerical value )e.g: amount, timestamp)
ObjectNull
ArrayEmpty array
BooleanFalse

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

RegionAbbreviationCode
IndonesiaIDco.id

Append the region code to the end of the domain.

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.