Message signing
The PayMe API utilizes the IETF Networking Group Draft "Signing HTTP Messages" specification with some modifications. In particular it uses the Signature HTTP header to ensure that a message from a client was not tampered with during transit.
The signature header
The "Signature" header value has the following components:
key Id (required): a string provided to a client that is passed to the API to look up the component it needs to validate the signature.
algorithm (optional): an IANA assigned signature algorithm . If provided this is compared with the algorithm the server stores along with the keyId, producing an error response if it does not match. For the PayMe API this is always hmac-sha256.
headers (required): a lower-cased, quoted list of HTTP header fields separated by a single space character. The special header name "(request-target)" is used to specify that the concatenated request method and path were used to create the signing string. The relative position of a header field name is important for correctly constructing the signing string for validating a message. The required headers for the PayMe API are detailed later in this document.
signature (required): the base 64 encoded output of the signature algorithm when applied to the signature string. The rest of this document explains how this is calculated.
Example:
Signature: keyId="1f989ca0-d9a9-4a54-85aa-d863aa98d2f1",algorithm="hmac-sha256",headers="(request-target) Api-Version Request-Date-Time Content-Type Digest Accept Trace-Id Authorization",signature="zfVXOHPmTt/eaNfO22N+BaRJLBFfN4Mm0nF+XppQgXs="
Important points to note:
Signature component fields are specified by =
The "headers" field value is a list of lowercase header names which are assumed to occur in the request/response headers with the exception of the special "(request-target)" pseudo header, referring to the request method and path.
In order to create the "signature" field value, a client MUST:
- Use the contents of the HTTP message, the `headers` value, and the Signature String Construction algorithm to create the signature string.
- The `algorithm` and key associated with `keyId` must then be used to generate a digital signature on the signature string.
- The `signature` is then generated by base 64 encoding the output of the digital signature algorithm.
Constructing the signing string
(The following description of the signature string construction algorithm is quoted verbatim from "Signing HTTP Messages, Section 2.3")
In order to generate the string that is signed with a key, the client MUST use the values of each HTTP header field in the `headers` Signature parameter, in the order they appear in the `headers` Signature parameter. It is out of scope for this document to dictate what header fields an application will want to enforce, but implementers SHOULD at minimum include the request target and Date header fields. To include the HTTP request target in the signature calculation, use the special `(request-target)` header field name.
- If the header field name is `(request-target)` then generate the header field value by concatenating the lowercased :method, an ASCII space, and the :path pseudo-headers (as specified in HTTP/2, Section 8.1.2.3 [7]).
Note: For the avoidance of doubt, lowercasing only applies to the :method pseudo-header and not to the :path pseudo-header. - Create the header field string by concatenating the lowercased header field name followed with an ASCII colon `:`, an ASCII space ` `, and the header field value. Leading and trailing optional whitespace (OWS) in the header field value MUST be omitted (as specified in RFC7230, Section 3.2.4 [8]).
- If value is not the last value then append an ASCII newline `\n`.
GET /foo HTTP/1.1 Host: example.org Date: Tue, 07 Jun 2014 20:51:35 GMT X-Example: Example header with some whitespace. Cache-Control: max-age=60 Cache-Control: must-revalidate |
For the HTTP request headers above, the corresponding signature string is:
(request-target): get /foo host: example.org date: Tue, 07 Jun 2014 20:51:35 GMT cache-control: max-age=60, must-revalidate x-example: Example header with some whitespace. |
Implementation specifics
The PayMe API has some implementation specific rules that must be followed.
Minimum required headers
There are a number of required headers that must be included in the request, these are detailed below.
Header | Request without body | Request with body | Response | Reason |
---|---|---|---|---|
(request-target) | Required | Required | Not required | Prevent the method and URI of the intended request from being altered |
request-date-time | Required | Required | Not required | Prevent Request-Date-Time from being altered Will help secure against a replay attack, as this value is used for timestamp verification |
content-type | Not required | Required | Conditional | Prevent Content-Type from being altered Condition: if message has body |
digest | Not required | Required | Conditional | Prevent message body hash from being altered Will prevent alteration of the message body, as doing so would change the digest The digest algorithm must be SHA-256 Condition: if message has body |
accept | Required | Required | Not required | Prevent Accept from being altered Condition: if message returns body |
trace-id | Required | Required | Not required | Prevent Trace-Id from being altered Will help secure against a replay attack, as this value is checked to ensure uniqueness within the past five minutes |
authorization | Required | Required | Not required | Prevent access token from being altered or replaced switch with a different user's access token |
api-version | Required | Required | Not required | Prevent a different Api-Version from being targeted |
Supported algorithms
The signature must be generated using the "hmac-sha256" algorithm.
Note that the signing key required by the hmac-sha256 algorithm is a binary value. However, the binary key will have been sent to you base 64 encoded in order to represent it as text.
Verifying a signature
(The following description of the signature string construction algorithm is quoted verbatim from "Signing HTTP Messages, Section 2.5")
In order to verify a signature, the HTTP message (request/response) receiving application MUST:
- Use the received HTTP message, the `headers` value, and the Signature String Construction algorithm to recreate the signature string.
- The `algorithm`, `keyId`, and base 64 decoded `signature` listed in the signature parameters are then used to verify the authenticity of the digital signature. Note: The application verifying the signature MUST derive the `algorithm` from the metadata associated with the `keyId` and MUST NOT use the value of `algorithm` from the signed message.
For example, assume that the `algorithm` value was "rsa-sha256". This would signal to the application that the metadata associated with `keyId` will express a RSA Public Key (as defined in RFC 3447), a signature string hashing function of SHA-256, and the `signature` verification algorithm to use to verify the signature is the one defined in RFC 3447, Section 8.2.2 [10].
The result of the signature verification algorithm specified in RFC 3447 should result in a successful verification unless the headers protected by the signature were tampered with in transit.
Digest header
When performing API operations that require the HTTP PUT or POST method to have a message body, the PayMe API requires that a Digest header be generated, and for that header to be used as part of the message signature. This adds an extra level of tamper-detection to HTTP messages received by the API.
The PayMe API supports only the SHA-256 digest algorithm, and the Digest header should be formatted as described in RFC3230 , Section 4.3.2. To convert the message body to binary as required by the SHA-256 algorithm, UTF-8 encoding should be used.
Worked Example
For the purposes of this example, the following credentials will be used:
Credential | Value |
---|---|
Signing ID | 1f989ca0-d9a9-4a54-85aa-d863aa98d2f1 |
Signing Key | RVhBTVBMRV9TSUdOSU5HX0tFWQ== (as base 64 encoded string) |
Access Token | eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IndVTG1ZZnNxZFF1V3RWXy1oeFZ0REpKWk00USIsImtpZCI6IndVTG1ZZnNxZFF1V3RWXy1oeFZ0REpKWk00USJ9.eyJhdWQiOiI5ZDBkOGI4Yi1lZTJiLTQ0ODgtYTUzMy05NmY2NmE0Zjg1NjkiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9jNDhjMGNlZi0zYzJkLTQ2NDctOTc2Zi1iODJlODRiOGJlMzcvIiwiaWF0IjoxNTQyNjEzMzA5LCJuYmYiOjE1NDI2MTMzMDksImV4cCI6MTU0MjYxNzIwOSwiYWlvIjoiNDJSZ1lKalNrTlgzNGZNN0EvZnEvNG9yRks4NUFnQT0iLCJhcHBpZCI6IjFmOTg5Y2EwLWQ5YTktNGE1NC04NWFhLWQ4NjNhYTk4ZDJmMSIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0L2M0OGMwY2VmLTNjMmQtNDY0Ny05NzZmLWI4MmU4NGI4YmUzNy8iLCJvaWQiOiJjOGM3MmVmNy1kMGI4LTQ5NjItYWUzMS0zYjkyODE4N2UyYmIiLCJyb2xlcyI6WyJtZXJjaGFudCIsInBheW1lbnRSZXF1ZXN0Il0sInN1YiI6ImM4YzcyZWY3LWQwYjgtNDk2Mi1hZTMxLTNiOTI4MTg3ZTJiYiIsInRpZCI6ImM0OGMwY2VmLTNjMmQtNDY0Ny05NzZmLWI4MmU4NGI4YmUzNyIsInV0aSI6Ik5SeE9yc3NTbDBHaGowRDZYYWRMQUEiLCJ2ZXIiOiIxLjAifQ.AWWGBdMM7OMRfkoqouKSYUCol2ER524QImB1goQiuzmOsg7dn8__N8rxR8yGseOxM95hEufev0wyAlyRdz_MGCx5aLfubU5k51ktYTbwNhfzRZtMnFeGkK2hCxxgWcoyqmPsvtRufFS2f7jjLhn5eT30LowpqNhWbQxhcq9--qQumF46bjFTrUdGiLqpzGGR9rckmASXxlBNGvKG-fLWm7xn-mMSM3rVDbjek9q6_EUF8qGTAScdeDBAwH-ts20EjicTAe8FMY1GqGcdAmvV9565ZOWwnDqncUxJockVgzhKIG-A2zIgrHYI0VOK2ZHKuRuiXqh-doS5yQQEAYxCqQ |
Suppose that you wish to call the payment request API to collect 100 HKD from a customer. The minimal message body to achieve this would be:
{"totalAmount":100.0,"currencyCode":"HKD"} |
Since the API call has a message body, a Digest header must be generated. Convert the message body to bytes using UTF-8 encoding and calculate the SHA-256 hash. base64Encode(sha256(utf8Bytes(messageBody)))
The resulting hash should be set as the Digest header.
Digest: SHA-256=gyT+LdIhNQLw2TjPTWRCWwIExmkRAoC2RvRM6FCggD4= |
It is also required to set the Trace-Id and Request-Date-Time headers to protect against replay attacks. For this example, we will set them to the values below.
Trace-Id: 46860f3b-6ce0-4063-b7ba-3ad14b209769 Request-Date-Time: 2018-11-19T07:46:53.271Z |
- (request-target)
- Api-Version
- Request-Date-Time
- Content-Type
- Digest
- Accept
- Trace-id
- Authorization
Note that in the signature string, all of the header names must be in lowercase. The resulting signature string is as follows.
(request-target): post /payments/paymentrequests api-version: 0.8 request-date-time: 2018-11-19T07:46:53.271Z content-type: application/json digest: SHA-256=gyT+LdIhNQLw2TjPTWRCWwIExmkRAoC2RvRM6FCggD4= accept: application/json trace-id: 46860f3b-6ce0-4063-b7ba-3ad14b209769 authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IndVTG1ZZnNxZFF1V3RWXy1oeFZ0REpKWk00USIsImtpZCI6IndVTG1ZZnNxZFF1V3RWXy1oeFZ0REpKWk00USJ9.eyJhdWQiOiI5ZDBkOGI4Yi1lZTJiLTQ0ODgtYTUzMy05NmY2NmE0Zjg1NjkiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9jNDhjMGNlZi0zYzJkLTQ2NDctOTc2Zi1iODJlODRiOGJlMzcvIiwiaWF0IjoxNTQyNjEzMzA5LCJuYmYiOjE1NDI2MTMzMDksImV4cCI6MTU0MjYxNzIwOSwiYWlvIjoiNDJSZ1lKalNrTlgzNGZNN0EvZnEvNG9yRks4NUFnQT0iLCJhcHBpZCI6IjFmOTg5Y2EwLWQ5YTktNGE1NC04NWFhLWQ4NjNhYTk4ZDJmMSIsImFwcGlkYWNyIjoiMSIsImlkcCI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0L2M0OGMwY2VmLTNjMmQtNDY0Ny05NzZmLWI4MmU4NGI4YmUzNy8iLCJvaWQiOiJjOGM3MmVmNy1kMGI4LTQ5NjItYWUzMS0zYjkyODE4N2UyYmIiLCJyb2xlcyI6WyJtZXJjaGFudCIsInBheW1lbnRSZXF1ZXN0Il0sInN1YiI6ImM4YzcyZWY3LWQwYjgtNDk2Mi1hZTMxLTNiOTI4MTg3ZTJiYiIsInRpZCI6ImM0OGMwY2VmLTNjMmQtNDY0Ny05NzZmLWI4MmU4NGI4YmUzNyIsInV0aSI6Ik5SeE9yc3NTbDBHaGowRDZYYWRMQUEiLCJ2ZXIiOiIxLjAifQ.AWWGBdMM7OMRfkoqouKSYUCol2ER524QImB1goQiuzmOsg7dn8__N8rxR8yGseOxM95hEufev0wyAlyRdz_MGCx5aLfubU5k51ktYTbwNhfzRZtMnFeGkK2hCxxgWcoyqmPsvtRufFS2f7jjLhn5eT30LowpqNhWbQxhcq9--qQumF46bjFTrUdGiLqpzGGR9rckmASXxlBNGvKG-fLWm7xn-mMSM3rVDbjek9q6_EUF8qGTAScdeDBAwH-ts20EjicTAe8FMY1GqGcdAmvV9565ZOWwnDqncUxJockVgzhKIG-A2zIgrHYI0VOK2ZHKuRuiXqh-doS5yQQEAYxCqQ |
Now calculate the HMAC-SHA256 signature of the signature string using your signing key.
base64Encode(hmac-sha256(base64Decode(signingKeyString), utf8Bytes(signatureString)))
zfVXOHPmTt/eaNfO22N+BaRJLBFfN4Mm0nF+XppQgXs= |
Finally, use the calculated signature in the Signature header. Note that it is important that the list of headers in the Signature header matches the order they were used in the signature string.
Signature: keyId="1f989ca0-d9a9-4a54-85aa-d863aa98d2f1",algorithm="hmac-sha256",headers="(request-target) Api-Version Request-Date-Time Content-Type Digest Accept Trace-Id Authorization",signature="zfVXOHPmTt/eaNfO22N+BaRJLBFfN4Mm0nF+XppQgXs=" |
Handling invalid signatures
- If the signature header is missing or malformed then the message should be considered unauthorised (401) and discarded.
- If the generated signature does not match the signature sent with the message, the message should be considered unauthorised (401) and discarded.
Resources
Standard implementations of this message signing standard can be found online at https://github.com/w3c-dvcg/http-signatures/issues/1 for most common languages.
© PayMe from HSBC | Terms & Conditions | Website Terms of Use | Privacy and Security
SVF License Number: SVFB002
Test Approach
As part of your PayMe API integration, you need to perform two phases of testing before you can launch with PayMe:
- Smart Sandbox Testing — to test your desktop web, mobile web or mobile application journeys in a simulated environment.
- Live Testing — to test your desktop web, mobile web or mobile application journeys with our PayMe live environment.
Test Evidence
To move from Smart Sandbox Testing to Live Testing, we require you to execute and evidence some basic test cases for your Sandbox integration. We will review this evidence before providing you with live credentials for you to complete Live testing.
Similarly, before you launch PayMe, you are required to perform some test cases in Live testing for us to review and approve.
There are two testing documents:
- PayMe_APIs_Test_Pack_v1.8.xlsx — constains the PayMe API integration test cases. For each test case, there are detailed step-by-step instructions that will help you replicate the scenarios you need to test.
- PayMe_API_Test_Execution_Evidence_v1.4.docx — a template to input the test execution evidence from your completed test cases.
Download both of these documents here.
Getting Started
Smart Sandbox Testing
- Select your relevant Sandbox test cases -
- In the PayMe_APIs_Test_Pack_v1.8 Excel file, refer to the Sandbox Test Summary tab to determine which test cases are applicable to your integration.
- Note that the test cases you need to complete vary depending on your integration, i.e., if you are only integrating PayMe with your mobile app, you don't need to perform the test cases for desktop web.
- Excute the test cases following the step-by-step instructions.
- Evidence the test execution in the PayMe_API_Test_Execution_Evidence_v1.4 Word document.
- E-mail the completed PayMe_APIs_Test_Execution_Evidence_v1.4 Word document to your Integration manager.
- Await feedback from PayMe Developer Support, make any necessary changes and submit evidence of the changes again.
- Await PayMe Developer Support approval to move to Live Testing (they will provide you with your live API credentials as well).
Live Testing
- Select your relevant Live test cases -
- In the PayMe_APIs_Test_Pack_v1.8 Excel file, refer to the Live Test Summary tab to determine which test cases are applicable to your integration.
- Note that the test cases you need to complete vary depending on your integration, i.e., if you are only integrating PayMe with your mobile app, you don't need to perform the test cases for desktop web.
- Excute the test cases following the step-by-step instructions.
- Evidence the test execution in the PayMe_API_Test_Execution_Evidence_v1.4 Word document.
- E-mail the completed PayMe_APIs_Test_Execution_Evidence_v1.4 Word document to your integration manager.
- Await feedback from PayMe Developer Support, make any necessary changes and submit evidence of the changes again.
- Wait PayMe Developer Support approval to launch.
- LAUNCH!!!
Smart Sandbox Scenarios
The Smart Sandbox allows you to make API calls against a simulated environment that fulfills the basic contract of the API. In addition, there are a number of scenarios built into the sandbox to allow you to test certain end-to-end desktop and mobile web/mobile app journeys.
To elicit certain behaviours from the sandbox, you need to use a particular cent amount when creating a payment request. These predefined cent amounts serve as “trigger numbers” to trigger the Sandbox to automatically replicate the associated built-in scenarios, such as the “happy flow” payment journey or error during payment request creation, etc.
Built-in Test Scenarios
All the built-in test scenarios that can be simulated in the Smart Sandbox are described in the Summary of Sandbox Test Scenarios table below. Please note that we don't need to see test evidence of all the scenarios listed on this page, just those test cases detailed in PayMe_APIs_Test_Pack_v1.8. Some of the scenarios below are solely provided to give you comfort in completing your integration or to make your testing easier.
No Trigger Number Entered
If you enter numbers other than those listed on this page, i.e., not the predefined trigger numbers, no scenarios will be triggered and there will be no special behaviour.
If the input to the API is valid, a payment request will be created and the PayCode will behave as normal. This means the PayCode will expire after its effective duration elapses.
If the input into the API is not valid, then an error will be returned. This applies to both desktop and mobile journeys.
Summary of Sandbox Test Scenarios
Desktop Journey Smart Sandbox Scenarios
Normal Expiry
Trigger: Any amount that ends with 80 cents e.g. 2.80
This scenario allows you to easily test your system's handling of an expired payment request.
When a payment request is created that triggers this scenario, the initial status will be Request for Payment Initiated, and the effectiveDuration will be the value as sent in the request.
If you call the get payment request within the effectiveDuration value, you should receive a Payment Request Initiated notification to the URL you specified in the request.
If you call the get payment request after the effectiveDuration value, you should receive a Payment Request Expired notification to the URL you specific in the request.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
"totalAmount": 2.80,
"currencyCode": "HKD",
"effectiveDuration": 600,
"notificationUri": "https://webhook.site/5875e490-dc34-4e8b-b577-3774f3763e09",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"merchantData": {
"orderId": "BR00234",
"orderDescription": "New order!",
"additionalData": "Order from: User2"
}
}
Response - HTTP 201{
"paymentRequestId": "3b8b08d2-a4c3-4ec2-be44-b754085bdbb4",
"totalAmount": "2.80",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/5875e490-dc34-4e8b-b577-3774f3763e09",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2020-12-17T10:13:18.799Z",
"effectiveDuration": 600,
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/3b8b08d2-a4c3-4ec2-be44-b754085bdbb4?appSuccessCallback=https://www.merchantsite.com/success",
"appLink": "https://qrcode-api-sandbox-pre.dpwaf.com/3b8b08d2-a4c3-4ec2-be44-b754085bdbb4?appSuccessCallback=https://www.merchantsite.com/success",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001",
"businessLogos": {
"tiny": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_40x40.png",
"small": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_60x60.png",
"normal": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_300x300.png",
"large": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_600x600.png",
"full": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo.png"
}
} - 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = 3b8b08d2-a4c3-4ec2-be44-b754085bdbb4within "effective Duration"
{
paymentRequestId": "3b8b08d2-a4c3-4ec2-be44-b754085bdbb4",
totalAmount": "2.80",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2020-12-17T10:13:18.799Z",
"effectiveDuration": 600,
"statusDescription": "Payment Request Initiated",
"statusCode": "PR001",
}after "effective Duration"
{
paymentRequestId": "3b8b08d2-a4c3-4ec2-be44-b754085bdbb4",
totalAmount": "2.80",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2020-12-17T10:13:18.799Z",
"effectiveDuration": 600,
"statusDescription": "Payment Request Expired",
"statusCode": "PR007",
}
Payment Success
Trigger: Any amount that ends with 81 cents eg. 2.81 or 3 cents e.g. 2.3
This scenario allows you to test your "happy flow" payment journey.
When a payment request is created that triggers this scenario, a success response for create payment request is received and the request status description will be Request for Payment Initiated. In less than 10 seconds, Smart Sandbox will update the status of payment to SUCCESS and will send a webhook notification on the URL mentioned by you when you created the payment request (notificationUri).
This notification message will show that the status of payment has been updated to success. Thereafter you can get the status of the payment by using GET API and the Payment Success status will be shown.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
"totalAmount": 2.81,
"currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "TH7856",
"orderDescription": "Thanksgiving Sale!",
"additionalData": "Order from : User1"
}
}
Response - HTTP 201{
"paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
"totalAmount": "2.81",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-04T07:41:14.662Z",
"effectiveDuration": 600,
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/b8b45fa2-06da-4d7a-b6af-cdbf198cb61b?appSuccessCallback=10.97.106.51/confirmation",
"appLink": "https://qrcode-api-sandbox-pre.dpwaf.com/b8b45fa2-06da-4d7a-b6af-cdbf198cb61b?appSuccessCallback=10.97.106.51/confirmation",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
"businessLogos": {
"tiny": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_40x40.png",
"small": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_60x60.png",
"normal": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_300x300.png",
"large": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_600x600.png",
"full": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo.png"
}
}
Webhook Notification Response{
"paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
"paymentRequestType": "Dynamic",
"totalAmount": "2.81",
"currencyCode": "HKD",
"createdTime": "2019-11-04T07:41:14.662Z",
"effectiveDuration": 600,
"statusDescription": "Payment Success",
"statusCode": "PR005",
"transactions":
[
{ "transactionId": "b54b419f-25ad-4d75-a7e1-ccf2c687e825",
"feeAmount": "0.0",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "003",
"transactionTypeDescription": "Collect payment",
"orderId": "TH7856",
"orderDescription": "Thanksgiving Sale!",
"transactionTime": "2019-11-04T07:41:14.662Z",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
}
]
}NOTE: For successful payments, the webhook response header will have a parameter x-event-type and its value will be payment.success.
- 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = b8b45fa2-06da-4d7a-b6af-cdbf198cb61b[NO BODY]
Response - HTTP 201
NOTE: This API has been enhanced in several versions of the API. Please check the response body corresponding to the version you use.
Version 0.11
{
paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
totalAmount": "2.81",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-04T07:41:15Z",
"effectiveDuration": 600,
"statusDescription": "Payment Success",
"statusCode": "PR005",
"transactions": [
"3db7b407-1de2-4196-bd47-5e5137cc2018"
]
}Version 0.12
{
paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
totalAmount": "2.81",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-04T07:41:15Z",
"effectiveDuration": 600,
"statusDescription": "Payment Success",
"statusCode": "PR005",
"transactions": [
"transactionId": "3db7b407-1de2-4196-bd47-5e5137cc2018",
"feeAmount": "0.087",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "003",
"transactionTypeDescription": "Collect payment",
"orderDescription": "",
"transactionTime": "2019-11-19T04:35:50Z",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
]
}
Payment Failure
Trigger: Any amount that ends with 77 cents e.g. 2.77
When a payment request is created that triggers this scenario, a success response for create payment request is received and the request status description will be Request for Payment Initiated.In less than 10 seconds, Smart Sandbox will update the status of payment to ERROR and will send a webhook notification on the URL mentioned by you when you created the payment request (notificationUri).
This notification message will show that the status of payment is still Request for Payment Initiated and the reasonDescription will show the reason for the failure of the payment request. Thereafter you can get the status of the payment by using GET API and the Request for Payment Initiated status will be shown.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
"totalAmount": 2.77,
"currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "BR00234",
"orderDescription": "New order!",
"additionalData": "Order from : User2"
}
}
Response - HTTP 201{
"paymentRequestId": "e47aa291-d473-4c9e-852f-2cc3ff9220db",
"totalAmount": "2.77",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-06T06:40:45.666Z",
"effectiveDuration": 600,
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/e47aa291-d473-4c9e-852f-2cc3ff9220db?appFailCallback=10.97.106.51/unsuccessful",
"appLink": "https://qrcode-api-sandbox-pre.dpwaf.com/e47aa291-d473-4c9e-852f-2cc3ff9220db?appFailCallback=10.97.106.51/unsuccessful",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
}
Webhook Notification Response{
"reasonCode": "EW2036",
"reasonDescription": "PayMe user Annual Pay Limit is reached.",
"paymentRequestId": "e8109f3f-9942-4a11-a657-cd9ef93eb806",
"paymentRequestType": "Dynamic",
"totalAmount": "2.77",
"currencyCode": "HKD",
"createdTime": "2019-11-08T09:22:44.296Z",
"effectiveDuration": 600,
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
}NOTE: For failed payments, the webhook response header will have a parameter x-event-type and its value will be payment.failure.
- 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = e47aa291-d473-4c9e-852f-2cc3ff9220db[NO BODY]
Response - HTTP 201{
paymentRequestId": "e47aa291-d473-4c9e-852f-2cc3ff9220db",
totalAmount": "2.77",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-06T06:40:46Z",
"effectiveDuration": 600,
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
}
Cancel Payment Request
From Version 0.12 we have introduced this API which allows you to cancel a payment request. This scenario allows you to test the Cancel Payment journey. When a payment request is created, a success response for create payment request is received and the request status description will be Request for Payment Initiated. This API can be used to cancel the payment request. The status API will then show that the status of request is Request for Payment Rejected.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
"totalAmount": "5.00",
"currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "BR00234",
"orderDescription": "New order!",
"additionalData": "Order from : User2"
}
}
Response - HTTP 201{
"paymentRequestId": "e47aa291-d473-4c9e-852f-2cc3ff9220db",
"totalAmount": "5.00",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-06T06:40:45.666Z",
"effectiveDuration": 600,
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/e47aa291-d473-4c9e-852f-2cc3ff9220db?appFailCallback=10.97.106.51/unsuccessful",
"appLink": "https://qrcode-api-sandbox-pre.dpwaf.com/e47aa291-d473-4c9e-852f-2cc3ff9220db?appFailCallback=10.97.106.51/unsuccessful",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
} - 2. Cancel payment request
-
Request - PUT /payments/paymentrequests/{paymentRequestId}/cancel
where {paymentRequestId} = e47aa291-d473-4c9e-852f-2cc3ff9220db[NO BODY]
Response - HTTP 201{
"paymentRequestId": "e47aa291-d473-4c9e-852f-2cc3ff9220db",
"statusDescription": "Request for Payment Rejected",
"statusCode": "PR004"
} - 3. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = e47aa291-d473-4c9e-852f-2cc3ff9220db[NO BODY]
Response - HTTP 201{
paymentRequestId": "e47aa291-d473-4c9e-852f-2cc3ff9220db",
totalAmount": "5.00",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-06T06:41 :46Z",
"effectiveDuration": 600,
"statusDescription": "Request for Payment Rejected",
"statusCode": "PR004"
}
Error when cancelling a payment request that is being processed
Trigger: Any amount that ends with 83 cents e.g. 2.83
This scenario allows you to test your system's handling of API errors that occur when cancelling a payment request that is being processed.
When a cancel payment request is created that triggers this scenario, an error response will be received.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
totalAmount": 2.83,
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "BR00234",
"orderDescription": "New order!",
"additionalData": "Order from : User2"
}
}
Response - HTTP 201{
"paymentRequestId": "e47aa291-d473-4c9e-852f-2cc3ff9220db",
"totalAmount": "2.83",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-06T06:40:45.666Z",
"effectiveDuration": 600,
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/e47aa291-d473-4c9e-852f-2cc3ff9220db?appFailCallback=10.97.106.51/unsuccessful",
"appLink": "https://qrcode-api-sandbox-pre.dpwaf.com/e47aa291-d473-4c9e-852f-2cc3ff9220db?appFailCallback=10.97.106.51/unsuccessful",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
} - 2. Cancel payment request
-
Request - PUT /payments/paymentrequests/{paymentRequestId}/cancel
where {paymentRequestId} = e47aa291-d473-4c9e-852f-2cc3ff9220db[NO BODY]
Response - HTTP 400{
"message": "QR code payment is currently being processed",
"errors":
[
{ "errorCode": "EB022",
"errorDescription": "The API backend has rejected your request as the QR code payment is currently being processed",
}
]
}
PayCode Expiry
Trigger: Any amount that ends with 82 cents e.g. 2.82
This scenario allows you to easily test your system's handling of an expired payment request.
When a payment request is created that triggers this scenario, the initial status will be Request for Payment Initiated, but the effectiveDuration will be zero regardless of the value sent when the request was created. You should receive a Payment Request Expired notification to the URL you specified in the payment request.
Note this is not an error.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
totalAmount": 2.82,
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "FH00164",
"orderDescription": "Brand New order!",
"additionalData": "Order from : User3"
}
}
Response - HTTP 201{
"paymentRequestId": "f7769916-126d-4672-a52f-0188c20e689f",
"totalAmount": "2.82",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-06T06:40:45.666Z",
"effectiveDuration": 0,
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/f7769916-126d-4672-a52f-0188c20e689f?appFailCallback=10.97.106.51/unsuccessful",
"appLink": "https://qrcode-api-sandbox-pre.dpwaf.com/f7769916-126d-4672-a52f-0188c20e689f?appFailCallback=10.97.106.51/unsuccessful",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
} - 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = f7769916-126d-4672-a52f-0188c20e689f[NO BODY]
Response - HTTP 200{
paymentRequestId": "f7769916-126d-4672-a52f-0188c20e689f",
totalAmount": "2.82",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-07-03T09:53:13Z",
"effectiveDuration": 0,
"statusDescription": "Payment Request Expired",
"statusCode": "PR007"
}
Error during Payment Request Creation
Trigger: Any amount that ends with 44 cents e.g. 2.44 or 4 cents eg. 2.4
This scenario allows you to test your system's handling of API errors at the point of payment request creation.
When a payment request is created that triggers this scenario, a back-end server error will be simulated by the API, which will be returned to you as an HTTP 400 error code.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
totalAmount": 2.44,
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "CG7778",
"orderDescription": "Re-order!",
"additionalData": "Order from : User4"
}
}
Response - HTTP 400{
"message": "",
"errors":
[
{ "errorCode": "EB099",
"errorDescription": "Internal Server Error, contact support",
}
]
}
Error when Checking Payment Request Status
Trigger: Any amount that ends with 45 cents e.g. 2.45 or 5 cents e.g. 2.5
This scenario allows you to test your system's handling of API errors that occur when checking the status of an existing payment request.
When a payment request is created that triggers this scenario, any subsequent status queries for that payment request ID will return an error from the back-end, which will be returned to you as an HTTP 400 error code.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
totalAmount": 2.45,
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "AB00823",
"orderDescription": "Christmas Sale!",
"additionalData": "Order from : User5"
}
}
Response - HTTP 201{
"paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
"totalAmount": "2.45",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-04T07:41:14.662Z",
"effectiveDuration": 600,
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/b8b45fa2-06da-4d7a-b6af-cdbf198cb61b?appSuccessCallback=10.97.106.51/confirmation",
"appLink": "https://qrcode-api-sandbox-pre-dpwaf.com/b8b45fa2-06da-4d7a-b6af-cdbf198cb61b?appSuccessCallback=10.97.106.51/confirmation",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
"businessLogos": {
"tiny": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_40x40.png",
"small": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_60x60.png",
"normal": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_300x300.png",
"large": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_600x600.png",
"full": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo.png"
}
} - 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = b8b45fa2-06da-4d7a-b6af-cdbf198cb61b[NO BODY]
Response - HTTP 400{
"message": "Resource Not Found!",
"errors":
[
{ "errorCode": "EB099",
"errorDescription": "Internal Server Error, contact support",
}
]
}
Checking the Status of a Missing PayCode
Trigger: Any amount that ends with 70 cents e.g. 2.70
This scenario allows you to test how your system would handle a PayCode that unexpectedly did not exist.
When a payment request is created that triggers this scenario, any subsequent status queries for that payment request ID will return simulated error from the back-end to indicate that the PayCode could not be found. Note that this is not a scenario that is expected to occur, but is provided to test that your system can gracefully handle the unexpected failure.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
totalAmount": 2.70,
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "KY00745",
"orderDescription": "Pre-Christmas Sale!",
"additionalData": "Order from : User6"
}
}
Response - HTTP 201{
"paymentRequestId": "0eaad41d-a217-434f-83de-61ce8c92aba0",
"totalAmount": "2.70",
"currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-07-08T08:37:35.15Z",
"effectiveDuration": 600,
"webLink": "http://qr-payme-dev.allyoupayclouds.com/2/DqrUHaIXQ0-D3mHOjJKroA",
"appLink": "payme://?id=DqrUHaIXQ0-D3mHOjJKroA",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
} - 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = 0eaad41d-a217-434f-83de-61ce8c92aba0[NO BODY]
Response - HTTP 400{
"message": "",
"errors":
[
{ "errorCode": "EB008",
"errorDescription": "PayCode not found",
}
]
}
Checking the Status of an Expired PayCode
Trigger: Any amount that ends with 71 cents e.g. 2.71
This scenario allows you to test how your system would handle a PayCode that was unexpectedly expired earlier than the requested effectiveDuration.
When a payment request is created that triggers this scenario, any subsequent status queries for that payment request ID will return a Payment Request Expired status from the back-end. Note that this is not a scenario that is expected to occur, but is provided to test that your system can gracefully handle the unexpected status.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
totalAmount": 2.71,
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "ZZ00698",
"orderDescription": "After-Christmas Sale!",
"additionalData": "Order from : User7"
}
}
Response - HTTP 201{
"paymentRequestId": "0c6cc092-87b8-4df5-81ec-a8b5c80c91ba",
"totalAmount": "2.71",
"currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-07-08T08:52:44.964Z",
"effectiveDuration": 600,
"webLink": "http://qr-payme-dev.allyoupayclouds.com/2/DGzAkoe4TfWB7Ki1yAyRug",
"appLink": "payme://?id=DGzAkoe4TfWB7Ki1yAyRug",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
} - 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = 0c6cc092-87b8-4df5-81ec-a8b5c80c91ba[NO BODY]
Response - HTTP 200{
paymentRequestId": "0c6cc092-87b8-4df5-81ec-a8b5c80c91ba",
totalAmount": "2.71",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-07-08T08:52:45Z",
"effectiveDuration": 600,
"statusDescription": "Payment Request Expired",
"statusCode": "PR007"
}
Mobile Journey Smart Sandbox Scenarios
Payment Success
Trigger: Any amount that ends with 81 cents e.g. 2.81
This scenario allows you to test a successful payment transaction. After entering the payment amount and selecting PayMe as the payment option, a Payment Initiation Page appears and you will be redirected to the Payment Success Page automatically, as follows:
The Sandbox completes the payment journey and returns a success URL that is set by you in appSuccessCallback. This URL should redirect user to the Order/Payment completion page of your mobile journey. The success URL will either be a deep link to your mobile application or it will open in your mobile web browser directly.
A webhook notificaiton will also be sent on the URL mentioned by you in notificationUri. This notification can be used by you to mark the payment as successful. For details about webhook, click here.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
"totalAmount": 2.81,
"currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "IN2300",
"orderDescription": "Summer Sale!",
"additionalData": "Order from : User9"
}
}
Response - HTTP 201{
"paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
"totalAmount": "2.81",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-04T07:41:14.662Z",
"effectiveDuration": 600,
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/b8b45fa2-06da-4d7a-b6af-cdbf198cb61b?appSuccessCallback=10.97.106.51/confirmation",
"appLink": "https://qrcode-api-sandbox-pre.dpwaf.com/b8b45fa2-06da-4d7a-b6af-cdbf198cb61b?appSuccessCallback=10.97.106.51/confirmation",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
"businessLogos": {
"tiny": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_40x40.png",
"small": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_60x60.png",
"normal": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_300x300.png",
"large": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_600x600.png",
"full": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo.png"
}
} - 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = b8b45fa2-06da-4d7a-b6af-cdbf198cb61b[NO BODY]
Response - HTTP 200{
paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
totalAmount": "2.81",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-04T07:41:15Z",
"effectiveDuration": 600,
"statusDescription": "Payment Success",
"statusCode": "PR005"
}
Payment Failure
Trigger: Any amount that ends with 77 cents e.g. 2.77 or 9 cents e.g. 2.9
This scenario allows you to test a failed payment transaction. After entering the payment amount and selecting PayMe as the payment option, a Payment Initiation Page appears and you will be redirected to the Payment Failure Page automatically, as follows:
The Sandbox completes the payment journey and returns a failure URL that is set by you in appFailCallback. This URL should redirect user to the Payment failure page of your mobile journey. The failure URL will either be a deep link to your mobile application or it will open in your mobile web browser directly.
A webhook notificaiton will also be sent on the URL mentioned by you in notificationUri. This notification can be used by you to mark the payment as failed. For details about webhook, click here.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
"totalAmount": 2.77,
"currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "FT7659",
"orderDescription": "Winter Sale!",
"additionalData": "Order from : User10"
}
}
Response - HTTP 201{
"paymentRequestId": "e47aa291-d473-4c9e-852f-2cc3ff9220db",
"totalAmount": "2.77",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-06T06:40:45.666Z",
"effectiveDuration": 600,
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/e47aa291-d473-4c9e-852f-2cc3ff9220db?appFailCallback=10.97.106.51/unsuccessful",
"appLink": "https://qrcode-api-sandbox-pre.dpwaf.com/e47aa291-d473-4c9e-852f-2cc3ff9220db?appFailCallback=10.97.106.51/unsuccessful",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
"businessLogos": {
"tiny": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_40x40.png",
"small": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_60x60.png",
"normal": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_300x300.png",
"large": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo_600x600.png",
"full": "https://shopfront.paymebiz.hsbc.com.hk/onboarding/df45d63700123b08cdbd5e7cf7c4264eb40abdf1dced3b2464e5a74d3d42a8f8/businessLogo.png"
}
} - 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = e47aa291-d473-4c9e-852f-2cc3ff9220db[NO BODY]
Response - HTTP 201{
paymentRequestId": "e47aa291-d473-4c9e-852f-2cc3ff9220db",
totalAmount": "2.77",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-06T06:40:46Z",
"effectiveDuration": 600,
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
}
Refund
This scenario allows you to test real-time refunds.
For successful refunds, there can be partial refund and full refund. For partial refund, the refundAmount will be smaller than the totalAmount. For full refund, the refundAmount will be equal to the totalAmount.
For unsuccessful refund, there can be two scenarios. The first is when the refundAmount is less than the mimimum permissible refund amount (0.01), the second is when the refundAmount is greater than the net refundable amount.
To test refunds, create a successful payment with trigger number 81 cents. Then you need to find the transactionId of the payment you have made. There are 3 ways to retrieve the transactionId of the payment transaction you want:
- Webhook Notification — After a payment is created and your customer pays, a webhook notification is sent on the URL mentioned by you while creating the payment request (notificationUri). This notification will give the payment status on the particular paymentRequestId along with the transactionId. The transactionId can then be used to identify the transcation you want to refund.
- Check Payment Status — You can use this API to to get the transactionId with the help of the paymentRequestId.
- Transaction List — You can use this API to get the transactionId where transactionType is 003.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
"totalAmount": "39.81",
"currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"effectiveDuration": "600",
"notificationUri": "https://webhook.site/149cbdf3-ab67-4013-bf7b-ed96463ee0bc",
"merchantData": {
"orderId": "ID12345678",
"orderDescription": "Pre-Winter Sale!",
"additionalData": "Order from : User11"
}
}
Response - HTTP 201{
"paymentRequestId": "64911955-3e8e-404b-b2fc-025b6bf726b1",
"totalAmount": "39.81",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/149cbdf3-ab67-4013-bf7b-ed96463ee0bc",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-12T06:10:32.515Z",
"effectiveDuration": "600",
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/64911955-3e8e-404b-b2fc-025b6bf726b1?appSuccessCallback=https://www.example.com/success",
"appLink": "https://qrcode-api-sandbox-pre.dpwaf.com/64911955-3e8e-404b-b2fc-025b6bf726b1?appSuccessCallback=https://www.example.com/success",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
}
Webhook Notification Response{
"paymentRequestId": "64911955-3e8e-404b-b2fc-025b6bf726b1",
"paymentRequestType": "Dynamic",
"totalAmount": "39.81",
"currencyCode": "HKD",
"createdTime": "2019-11-12T06:10:32.515Z",
"effectiveDuration": 600,
"statusDescription": "Payment Success",
"statusCode": "PR005",
"transactions":
[
{ "transactionId": "041b5b32-5f91-4bb6-9119-3c2d33778f2c",
"feeAmount": "1",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "003",
"transactionTypeDescription": "Collect payment",
"orderId": "ID12345678",
"orderDescription": "Pre-Winter Sale!",
"transactionTime": "2019-11-12T06:10:33.000Z",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
}
]
} - 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = 64911955-3e8e-404b-b2fc-025b6bf726b1[NO BODY]
Response - HTTP 201NOTE: This API has been enhanced in several versions of the API. Please check the response body corresponding to the version you use.
Version 0.11
{
paymentRequestId": "64911955-3e8e-404b-b2fc-025b6bf726b1",
totalAmount": "39.81",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-12T06:10:33.000Z",
"effectiveDuration": 600,
"statusDescription": "Payment Success",
"statusCode": "PR005",
"transactions": [
"041b5b32-5f91-4bb6-9119-3c2d33778f2c"
]
}Version 0.12
{
paymentRequestId": "64911955-3e8e-404b-b2fc-025b6bf726b1",
totalAmount": "39.81",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-12T06:10:33.000Z",
"effectiveDuration": 600,
"statusDescription": "Payment Success",
"statusCode": "PR005",
"transactions": [
"transactionId": "041b5b32-5f91-4bb6-9119-3c2d33778f2c",
"feeAmount": "0.087",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "003",
"transactionTypeDescription": "Collect payment",
"orderDescription": "",
"transactionTime": "2019-11-12T06:10:33.000Z",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
]
} - 3. Request transaction list
-
Request - GET /payments/transactions API
[NO BODY]
Response - HTTP 201{
"transactions":
[
{ "transactionId": "041b5b32-5f91-4bb6-9119-3c2d33778f2c",
"payerId": "ff00000000000000000004",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "003",
"transactionTypeDescription": "Collect payment",
"transactionTime": "2019-11-12T06:10:33Z"
"transactionAmount": "39.81"
"transactionCurrencyCode": "HKD"
"feeAmount": "1",
"feeCurrencyCode": "HKD",
"statusCode": "001",
"statusDescription": "Success",
"payerName": "Payer 4",
"orderDescription": "Pre-Winter Sale!",
"orderId": "ID12345678",
"refundable": true,
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
}
]
}
Partial Refund
You can request to refund part of the amount of the transaction by referencing the transactionId.
Example:
- 1. Request partial refund
-
Request - POST /payments/transactions/{transactionsId}/refunds
where {transactionId} = 041b5b32-5f91-4bb6-9119-3c2d33778f2c{
"amount": 2.00,
"reasonMessage": "Incorrect size",
"reasonCode": "01",
"currencyCode": "HKD",
}
Response - HTTP 201{
"refundId": "0522add6-76bb-4399-898b-e4e56a809637",
"transactionId": "041b5b32-5f91-4bb6-9119-3c2d33778f2c",
"payerId": "ff00000000000000000004",
"refundAmount": "2.00",
"refundCurrencyCode": "HKD",
"feeAmount": "0.03",
"feeCurrencyCodee": "HKD",
"reasonCode": "01",
"reasonMessage": "Incorrect size",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
}
Full Refund
You can request to refund the entire amount of the transaction by referencing the transactionId.
Example:
- 1. Request full refund
-
Request - POST /payments/transactions/{transactionsId}/refunds
where {transactionId} = 041b5b32-5f91-4bb6-9119-3c2d33778f2c{
"amount": 37.81,
"reasonMessage": "Incorrect size",
"reasonCode": "01",
"currencyCode": "HKD",
}
Response - HTTP 201{
"refundId": "81170756-ec28-4b6c-9878-eebe608a6c62",
"transactionId": "041b5b32-5f91-4bb6-9119-3c2d33778f2c",
"payerId": "ff00000000000000000004",
"refundAmount": "37.81",
"refundCurrencyCode": "HKD",
"feeAmount": "0.567",
"feeCurrencyCodee": "HKD",
"reasonCode": "01",
"reasonMessage": "Incorrect size",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
}
Error when Refund Amount is Greater than Transaction Amount
If the amount you try to refund is bigger than the transaction amount, you will get an error message.
Example:
- 1. Refund amount requested > transaction amount
-
Request - POST /payments/transactions/{transactionsId}/refunds
where {transactionId} = 041b5b32-5f91-4bb6-9119-3c2d33778f2c{
"amount": 100.81,
"reasonMessage": "Incorrect size",
"reasonCode": "01",
"currencyCode": "HKD",
}
Response - HTTP 400{
"message": "",
"errors":
[
{ "errorCode": "EB010",
"errorDescription": "Refund amount entered > net refundable amount",
}
]
}
Error when Refund Amount is Greater than Wallet Balance
If the amount you try to refund is larger than the wallet balance, you will get an error message.
Trigger: A transaction is created with amount 9902.00 and then a refund is performed for amount 9902.00
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
"totalAmount": 9902.00,
"currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"effectiveDuration": 600,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "TH7856",
"orderDescription": "Thanksgiving Sale!",
"additionalData": "Order from : User1"
}
}
Response - HTTP 201{
"paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
"totalAmount": "9902.00",
"currencyCode": "HKD",
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-04T07:41:14.662Z",
"effectiveDuration": 600,
"webLink": "https://qrcode-api-sandbox-pre.dpwaf.com/b8b45fa2-06da-4d7a-b6af-cdbf198cb61b?appSuccessCallback=10.97.106.51/confirmation",
"appLink": "https://qrcode-api-sandbox-pre.dpwaf.com/b8b45fa2-06da-4d7a-b6af-cdbf198cb61b?appSuccessCallback=10.97.106.51/confirmation",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
}
Webhook Notification Response{
"paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
"paymentRequestType": "Dynamic",
"totalAmount": "9902",
"currencyCode": "HKD",
"createdTime": "2019-11-04T07:41:14.662Z",
"effectiveDuration": 600,
"statusDescription": "Payment Success",
"statusCode": "PR005",
"transactions":
[
{ "transactionId": "b54b419f-25ad-4d75-a7e1-ccf2c687e825",
"feeAmount": "0.0",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "003",
"transactionTypeDescription": "Collect payment",
"orderId": "TH7856",
"orderDescription": "Thanksgiving Sale!",
"transactionTime": "2019-11-04T07:41:14.662Z",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
}
]
}NOTE: For successful payments, the webhook response header will have a parameter x-event-type and its value will be payment.success.
- 2. Check payment status
-
Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = b8b45fa2-06da-4d7a-b6af-cdbf198cb61b[NO BODY]
Response - HTTP 201
NOTE: This API has been enhanced in several versions of the API. Please check the response body corresponding to the version you use.
Version 0.11
{
paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
totalAmount": "9902.00",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-04T07:41:15Z",
"effectiveDuration": 600,
"statusDescription": "Payment Success",
"statusCode": "PR005",
"transactions": [
"3db7b407-1de2-4196-bd47-5e5137cc2018"
]
}Version 0.12
{
paymentRequestId": "b8b45fa2-06da-4d7a-b6af-cdbf198cb61b",
totalAmount": "9902.00",
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-11-04T07:41:15Z",
"effectiveDuration": 600,
"statusDescription": "Payment Success",
"statusCode": "PR005",
"transactions": [
"transactionId": "3db7b407-1de2-4196-bd47-5e5137cc2018",
"feeAmount": "0.087",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "003",
"transactionTypeDescription": "Collect payment",
"orderDescription": "",
"transactionTime": "2019-11-19T04:35:50Z",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
]
} - 3. Perform a refund
-
Request - POST /payments/transactions/{transactionsId}/refunds
where {transactionId} = 041b5b32-5f91-4bb6-9119-3c2d33778f2c{
"amount": 9902.00,
"reasonMessage": "Incorrect size",
"reasonCode": "01",
"currencyCode": "HKD",
}
Response - HTTP 400{
"message": "",
"errors":
[
{ "errorCode": "EB011",
"errorDescription": "Refund amount entered > wallet balance",
}
]
}
NOTE: Partial refunds i.e. refund amount < 9902.00 will be successful and act normally
Error when Refund Amount is Less than Minimum Transaction Amount (0.01)
If the amount you try to refund is less than the minimum refund amount (0.01), you will get an error message.
Example:
- 1. Refund amount requested < 0.01
-
Request - POST /payments/transactions/{transactionsId}/refunds
where {transactionId} = 041b5b32-5f91-4bb6-9119-3c2d33778f2c{
"amount": 0.001,
"reasonMessage": "Incorrect size",
"reasonCode": "01",
"currencyCode": "HKD",
}
Response - HTTP 400{
"message": "Service Request Validation Failed",
"errors":
[
{ "errorCode": "EA018",
"errorDescription": "Field error in object 'refundInputModel': field 'amount' must be greater than or equal to 0.01; rejected value [0.001]",
}
]
}
Transaction Reporting
This scenario allows you to test generating the transaction history of a list of successful payment and refund transactions. Failed payments will not be included in the transaction history.
All transactions are listed according to their transaction IDs, i.e., transactionId.
Example:
- 1. Request transaction report
-
Request - GET /payments/transactions API
[NO BODY]
Response - HTTP 201{
"transactions":
[
{ "transactionId": "81170756-ec28-4b6c-9878-eebe608a6c62",
"payerId": "ff00000000000000000004",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "004",
"transactionTypeDescription": "Refund",
"transactionTime": "2019-11-12T06:39:10Z"
"transactionAmount": "37.81"
"transactionCurrencyCode": "HKD"
"feeAmount": "0.567",
"feeCurrencyCode": "HKD",
"statusCode": "001",
"statusDescription": "Success",
"payerName": "Payer 4",
"orderDescription": "Incorrect size",
"orderId": "ID12345678",
"refundable": false,
"reasonCode": "01",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
}
{ "transactionId": "0522add6-76bb-4399-898b-e4e56a809637",
"payerId": "ff00000000000000000004",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "004",
"transactionTypeDescription": "Refund",
"transactionTime": "2019-11-12T06:35:37Z"
"transactionAmount": "2.00"
"transactionCurrencyCode": "HKD"
"feeAmount": "0.03",
"feeCurrencyCode": "HKD",
"statusCode": "001",
"statusDescription": "Success",
"payerName": "Payer 4",
"orderDescription": "Incorrect size","orderId": "ID12345678",
"orderId": "ID12345678",
"refundable": false,
"reasonCode": "01",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
}
{ "transactionId": "041b5b32-5f91-4bb6-9119-3c2d33778f2c",
"payerId": "ff00000000000000000004",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "003",
"transactionTypeDescription": "Collect payment",
"transactionTime": "2019-11-12T06:10:33Z"
"transactionAmount": "39.81"
"transactionCurrencyCode": "HKD"
"feeAmount": "1",
"feeCurrencyCode": "HKD",
"statusCode": "001",
"statusDescription": "Success",
"payerName": "Payer 4",
"orderDescription": "Pre-Winter Sale!",
"orderId": "ID12345678",
"refundable": true,
"reasonCode": "01",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
}
]
}
Get Transaction Details by ID
From Version 0.12 we have introduced this API which will enable you to get the details of transaction from the transaction Id. This API will also return the associated refund transactions.
It is only possible for a payment request to have one associated payment transaction. These transactions will have the transactionType=003 and transactionTypeDescription=Collect payment. However, for each payment request there can be multiple refund transactions. These transactions will have the transactionType=004 and transactionTypeDescription=Refund.
NOTE: The transaction list will be sorted by transaction date time.
Example:
- 1. Get Transaction Details by ID
-
Request - GET /transactions/{transactionId} where {transactionId} = 041b5b32-5f91-4bb6-9119-3c2d33778f2c
[NO BODY]
Response - HTTP 201 (Without Refund Transactions){
"transactions":
[
{ "transactionId": "041b5b32-5f91-4bb6-9119-3c2d33778f2c",
"payerId": "ff00000000000000000004",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "003",
"transactionTypeDescription": "Collect payment",
"transactionTime": "2019-11-12T06:10:33Z"
"transactionAmount": "39.81"
"transactionCurrencyCode": "HKD"
"feeAmount": "1",
"feeCurrencyCode": "HKD",
"statusCode": "001",
"statusDescription": "Success",
"payerName": "Payer 4",
"orderDescription": "Pre-Winter Sale!",
"orderId": "ID12345678",
"refundable": true,
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee"
}
],
"listDate": "2019-11-19T08:35:55.717Z"
}
Response - HTTP 201 (With Refund Transactions){
"transactions":
[
{ "transactionId": "041b5b32-5f91-4bb6-9119-3c2d33778f2c",
"payerId": "ff00000000000000000004",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "003",
"transactionTypeDescription": "Collect payment",
"transactionTime": "2019-11-12T06:10:33Z"
"transactionAmount": "39.81"
"transactionCurrencyCode": "HKD"
"feeAmount": "1",
"feeCurrencyCode": "HKD",
"statusCode": "001",
"statusDescription": "Success",
"payerName": "Payer 4",
"orderDescription": "Pre-Winter Sale!",
"orderId": "ID12345678",
"refundable": true,
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee
}
{ "transactionId": "0522add6-76bb-4399-898b-e4e56a809637",
"payerId": "ff00000000000000000004",
"transactionSource": "005",
"transactionSourceDescription": "Online",
"transactionType": "004",
"transactionTypeDescription": "Refund",
"transactionTime": "2019-11-12T06:35:37Z"
"transactionAmount": "2.00"
"transactionCurrencyCode": "HKD"
"feeAmount": "0.03",
"feeCurrencyCode": "HKD",
"statusCode": "001",
"statusDescription": "Success",
"payerName": "Payer 4",
"orderDescription": "Incorrect size",
"orderId": "ID12345678",
"refundable": false
"reasonCode": "01",
"walletIndicatorId": "95d99be474c3d7436c888626888eaed81196d9438848bf49e70f563c912854ee
}
],
"listDate": "2019-11-19T08:35:55.717Z"
}
Bootstrap Data in Smart Sandbox
The Sandbox also supports a special request to generate bootstrap data. This request erases any payment requests and transactions that are created by your Sandbox account, and replaces them with simulated historical data for the 60 days prior to when you send the request. The bootstrap data is intended to allow you to easily test the transaction API by giving you data to query against without having to manually create them yourself.
Reset Payment Records and Generate Bootstrap Data
Trigger: Create a payment request for the specific amount 444444.99.
This special scenario will reset the sandbox, deleting all existing payment requests and transactions, and generate random bootstrap data for the previous 60 days. The bootstrap data will contain a selection of payment requests and transactions for random small amounts and with random small fees applied.
Note that this request can take approximately 30 seconds to return and the paymentRequestId is set to the null UUID 00000000-0000-0000-0000-000000000000 to indicate that it is not a standard scenario.
Example:
- 1. Create payment request
-
Request - POST /payments/paymentrequests
{
totalAmount": 444444.99,
currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
effectiveDuration": 0,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"merchantData": {
"orderId": "KK00843",
"orderDescription": "Easter Sale!",
"additionalData": "Order from : User8"
}
}Response - HTTP 201
{
"paymentRequestId": "00000000-0000-0000-0000-000000000000",
"totalAmount": "444444.99",
"currencyCode": "HKD",
"appSuccessCallback": "https://www.merchantsite.com/success",
"appFailCallback": "https://www.merchantsite.com/failure",
"createdTime": "2019-07-08T09:47:33.523Z",
"effectiveDuration": 0,
"notificationUri": "https://webhook.site/2f6552af-3f1a-4191-810e-69f7d548fb74",
"webLink": "http://qr-payme-dev.allyoupayclouds.com/2/AAAAAAAAAAAAAAAAAAAAAA",
"appLink": "payme://?id=DGzAKoe$TfWB7KilyAyRug",
"statusDescription": "Request for Payment Initiated",
"statusCode": "PR001"
}
© PayMe from HSBC | Terms & Conditions | Website Terms of Use | Privacy and Security
SVF License Number: SVFB002
Design guidelines
Use the following policies, guidelines and best practices to help you integrate the PayMe experience into your website.
Showing you accept PayMe
Firstly, show your users that you accept PayMe in your footer or listed payment methods
Footer example
When displaying the PayMe logo as a payment method, please follow the rules below:
Use the red PayMe logo on white or grey backgrounds.
Use the white PayMe logo on dark backgrounds.
Use grey for consistency if that's how you display other methods.
Avoid using the logo anywhere there is a poor contrast with the background color.
Selecting PayMe as a payment option
Payment selection
You may want to use clickable badges for a user to select their payment method, alternatively you can use our 'Pay with PayMe' button.
You can also display PayMe in a list of radio buttons.
If you are using a dropdown list, we advise that you use the native behaviour on mobile devices to create a consisten experience for your customers.
Ensure that the payment option reads PayMe, no space. The P and M must be capitalised.
You may follow and use your website / mobile app checkout button.
If your checkout button dynamically changes depending on the payment method, please use the button below.
Assets
PayMe logo
Please use the following PayMe logos for displaying payment method:
Primary PayMe logo (Recommend)
Alternative PayMe logo
White PayMe logo
Payme Logo red with HSBC
Buttons
Depending on your site's colour scheme, we suggest using the primary PayMe button wherever possible.
Primary button
Colour variations
White
Dark alternative
Making a payment
On mobile
For checkout on mobile devices, users will be redirected to the PayMe app.
We suggest displaying the following notification to illustrate the concept.
You have selected PayMe as your payment method and will be redirected to the PayMe app to complete payment.
The following table provides translations of the app switch message in Traditional Chinese and Simplified Chinese for your reference:
English | Traditional Chinese | Simplified Chinese |
---|---|---|
You have selected PayMe as your payment method and will be redirected to the PayMe app to complete payment. | 您已選擇以PayMe作為付款方式,並且將被重新定向至PayMe應用程式以完成付款。 | 您已选择以PayMe作为付款方式,并且将被重新定向至PayMe应用程式以完成付款。 |
You may want to display the above app switch message under the payment option or use an overlay.
On desktop
The PayMe payment experience is based on QR codes, or, using our terminology, PayCodes. Since not all users are familiar with it, we recommend that you include some instructions to guide users through the payment process.
The process for paying with PayMe involves 3 steps, as follows:
1. Open the PayMe app.
2. Scan the PayCode to authorise payment.
3. Complete payment in the app and wait for confirmation.
There are two ways to implement PayCodes, as described below:
Method 1: Display the PayCode within your checkout page once your customer has selected PayMe as their payment method.
Customers then scan the PayCode and complete payment within the app.
Method 2: Once the customer has selected PayMe and clicked a button to proceed to pay, display the PayCode on a new page.
Customers then scan and complete the transaction within the app.
The following table shows the Traditional Chinese and Simplified Chinese translations for the text elements in the above picture:
English | Traditional Chinese | Simplified Chinese |
---|---|---|
Scan this PayCode with PayMe | 請用 PayMe 掃描此 PayCode | 请用 PayMe 扫描此 PayCode |
Please do not close this page before payment is complete | 在交易完成前請不要關閉此頁面 | 在交易完成前请不要关闭此页面 |
Paying with PayMe | 如何用 PayMe 付款? | 如何用 PayMe 付款? |
1. Open the PayMe app. | 1. 打開 PayMe 應用程式 | 1. 打开 PayMe 应用程序 |
2. Scan the PayCode to authorise payment. | 2. 掃描 PayCode 以授權付款 | 2. 扫描 PayCode 以授权付款 |
3. Complete payment in the app and wait for confirmation. | 3. 在 PayMe 完成交易後,請留在此頁面等待確認 | 3. 在 PayMe 完成交易后,请留在此页面等待确认 |
Click here to download the Traditional Chinese and Simplified Chinese versions of the above PayCode page image.
Dynamically displaying the correct payment instructions
It's important to display the correct instructions for the device the customer is using. If you're unable to dynamically display this then you should show both sets of instructions for payment on mobile and desktop.
Error handling
To keep users informed and reassured, we suggest including a countdown timer and support message if the payment fails.
Error messages
Clear error messaging that explains the reason for a transaction failure or a server issue from PayMe's side can help customers quickly resolve issues and reduce frustration.
Error code | Reason | Message |
---|---|---|
EA001 | Merchant cannot get a token | Sorry, PayMe is currently unavailable but we're working hard to fix it! Please try again later or use another payment option. |
EA002 | Merchant cannot create a payment request | Sorry, PayMe is currently unavailable but we're working hard to fix it! Please try again later or use another payment option. |
EA003 | Merchant cannot check the status of an existing payment request | Sorry, PayMe is currently unavailable but we're working hard to fix it! Please try again later or use another payment option. |
EA004 | When a PayCode expires | Sorry, we were unable to confirm your payment status. Please contact the store to manually confirm the status of your order. |
Asset Library
Download a complete set of assets here:
PayCode generator
Our PayMe branded QR codes are part of the PayMe payment experience. We require you to use PayCodes and not standard QR codes during the payment process. A JavaScript PayCode generator implementation is provided as a reference below.
PayCode minimum size
For tablet and desktop experiences, the PayCode will be displayed accordingly for users to scan. We recommended the minimum size for PayCode is 250px by 250px.
Code samples
HTML
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=Utf-8">
<script type="text/javascript" src="qrcode.js"></script>
<script type="text/javascript" src="paycode.js"></script>
<script type="text/javascript" src="example.js"></script>
<!--STYLE-->
<link rel="stylesheet" type="text/css" href="style/style.css">
<title>PayCode Generator</title>
</head>
<body onload="update_qrcode()">
<h1>PayMe PayCode Generator</h1>
<form name="qrForm" onsubmit="update_qrcode();return false;">
<span>TypeNumber:</span>
<select name="t" onchange="update_qrcode()">
<option value="0" selected="selected">Auto Detect</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="40">40</option>
</select>
<span>ErrorCorrectionLevel:</span>
<select name="e" onchange="update_qrcode()">
<option value="L">L(7%)</option>
<option value="M">M(15%)</option>
<option value="Q" selected="selected">Q(25%)</option>
<option value="H">H(30%)</option>
</select>
<span>Size:</span>
<input name="size" type="text" value="344"/>
<br/>
<span>Logo:</span>
<input type="file" onchange="encodeImageFileAsURL(this)" />
<span>
<img id="logo" width="30" height="30" src="./Icon-192.png" onload="update_qrcode()">
</span>
<input type="checkbox" name="consumer" value="consumer" onchange="update_qrcode()"> PayMe Style
<br>
<p/>
<textarea name="msg" rows="2" cols="86">https://qr.payme.hsbc.com.hk/2/ThisIsAnExamplePayCode</textarea<
<p/>
<input class="update-btn" type="button" value="Update" onclick="update_qrcode()"/>
<div id="qr"></div>
<canvas id="payCodeCanvas" width="344" height="344"></canvas>
</form>
<footer>
© PayMe from HSBC | <a href="https://payme.hsbc.com.hk/terms-and-conditions" rel="noopener noreferrer" target="blank">Terms & Conditions</a>
| <a href="https://payme.hsbc.com.hk/pws-terms" rel="noopener noreferrer" target="blank">Website Terms of Use</a>
| <a href="https://payme.hsbc.com.hk/pws-privacy-security" rel="noopener noreferrer" target="blank">Privacy and Security</a>
<br>
SVF License Number: SVFB002
</footer>
</body>
</html>
CSS
body {
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica', system-ui, sans-serif;
margin: 0 auto;
max-width: 960px;
padding: 15px;
font-size: 16px;
color: #191313;
}
input[type=text] {
padding: 4px;
border: #cfcfcf 1px solid;
}
input[type=file] {
font-size: 12px;
}
#logo {
margin-bottom: -9px;
}
select {
padding: 5px 10px;
margin-right: 10px;
min-width: 120px;
font-size: 12px;
border: none;
border-radius: 4px;
background-color: #f1f1f1;
margin-bottom: 10px;
display: inline-block;
}
textarea {
font-size: 14px;
padding: 10px;
font-family: 'Courier New', Courier, monospace;
border: #cfcfcf 1px solid !important;
}
.update-btn {
display: inline-block;
background-color: #ffffff;
border: 1px solid #db0011;
color: #db0011;
font-size: 16px;
border-radius: 28px;
padding: 10px 50px;
margin: 10px auto;
position: relative;
cursor: pointer;
}
.update-btn:hover {
background-color: #f2f2f2;
}
footer {
margin: 0 auto;
padding: 30px 0px;
font-size: 12px;
}
JavaScript
var update_qrcode = function() {
var form = document.forms['qrForm'];
var text = form.elements['msg'].value.replace(/^[\s\u3000]+|[\s\u3000]+$/g, '');
var t = form.elements['t'].value;
var e = form.elements['e'].value;
var m = 'Byte';
var mb = 'UTF-8';
var qr = create_qrcode(text, t, e, m, mb);
var size = form.elements['size'].value;
var consumer = form.elements['consumer'].checked;
var canvas = document.getElementById('payCodeCanvas');
var ctx = canvas.getContext('2d');
var logo = document.getElementById('logo');
canvas.width = size;
canvas.height = size;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawPayCode(qr, canvas, 7, logo, consumer);
};
© PayMe from HSBC | Terms & Conditions | Website Terms of Use | Privacy and Security
SVF License Number: SVFB002