Message signing

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:

  1. Use the contents of the HTTP message, the `headers` value, and the Signature String Construction algorithm to create the signature string.
  2. The `algorithm` and key associated with `keyId` must then be used to generate a digital signature on the signature string.
  3. 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.

 

  1. 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.
  2. 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 RFC7230Section 3.2.4 [8]).
  3. 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:

  1. Use the received HTTP message, the `headers` value, and the Signature String Construction algorithm to recreate the signature string.
  2. 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 3447Section 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
Client 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
The signature string is created by combining the required headers from the request. Since this API call has a message body and expects a response body, the required headers will be:
  1. (request-target)
  2. Api-Version
  3. Request-Date-Time
  4. Content-Type
  5. Digest
  6. Accept
  7. Trace-id
  8. 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

  1. If the signature header is missing or malformed then the message should be considered unauthorised (401) and discarded.
  2. 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

 

API Integration Testing

Test Approach

As part of your PayMe API integration, you need to perform two phases of testing before you can launch with PayMe:

  1. Smart Sandbox Testing — to test your desktop web, mobile web or mobile application journeys in a simulated environment.
  2. 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:

  1. 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.
  2. 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

  1. Select your relevant Sandbox test cases - 
    1. 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.
    2. 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.
  2. Excute the test cases following the step-by-step instructions.
  3. Evidence the test execution in the PayMe_API_Test_Execution_Evidence_v1.4 Word document.
  4. E-mail the completed PayMe_APIs_Test_Execution_Evidence_v1.4 Word document to your Integration manager.
  5. Await feedback from PayMe Developer Support, make any necessary changes and submit evidence of the changes again.
  6. Await PayMe Developer Support approval to move to Live Testing (they will provide you with your live API credentials as well).

Live Testing

  1. 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.
  2. Excute the test cases following the step-by-step instructions.
  3. Evidence the test execution in the PayMe_API_Test_Execution_Evidence_v1.4 Word document.
  4. E-mail the completed PayMe_APIs_Test_Execution_Evidence_v1.4 Word document to your integration manager.
  5. Await feedback from PayMe Developer Support, make any necessary changes and submit evidence of the changes again.
  6. Wait PayMe Developer Support approval to launch.
  7. 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

Scenario Journey Trigger Number (cents) Test Case Submission Required Test Case ID
Normal expiry Desktop 80 Yes SandboxTC_1002
Payment success Desktop 81 Yes SandboxTC_1001
Payment failure Desktop 77 No N/A
Error when cancelling a payment request that is being processed Desktop 83 No N/A
PayCode expiry Desktop 82 No N/A
Error during payment request creation Desktop 44 Yes SandboxTC_1003
Error when checking payment request status Desktop 45 Yes SandboxTC_1004
Checking the status of a missing PayCode Desktop 70 No N/A
Checking the status of an expired PayCode Desktop 71 No N/A
Payment success Mobile web/
Mobile app
81 Yes SandboxTC_1005/
SandboxTC_1007
Payment failure Mobile web/
Mobile app
77 Yes SandboxTC_1006/
SandboxTC_1008
Partial refund Refund N/A No N/A
Full refund Refund N/A No N/A
Error when refund amount is greater than transaction amount Refund N/A No N/A
Error when refund amount is larger than the wallet balance Refund 9902.00 No N/A
Error when refund amount is less than minimum transaction amount (0.01) Refund N/A No N/A
Transaction reporting Reporting N/A No N/A
Get Transaction Details by ID Reporting N/A No N/A
Reset payment records and generate bootstrap data N/A N/A No N/A

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-b754085bdbb4

within "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 e.g. 2.81

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"
      }
  ]
}

 

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"
  ]
}

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

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

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:

 

Payment Success Page

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

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:

 

Payment Failure Page

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:

  1. 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.
  2. Check Payment Status — You can use this API to to get the transactionId with the help of the paymentRequestId.
  3. 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"
      }
  ]
}

2. Check payment status

Request - GET /payments/paymentrequests/{paymentRequestId}
where {paymentRequestId} = 64911955-3e8e-404b-b2fc-025b6bf726b1

[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": "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"
  ]
}

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
      }
  ]
}

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",
}

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",
}

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"
      }
  ]
}

 

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"
  ]
}

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"
      }
      {  "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"
      }
      {  "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
      }
  ]
}

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
      }
  ],
  "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
      }
      {  "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"
      }
  ],
      "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

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
Show that you accept PayMe.

Footer example

When displaying the PayMe logo as a payment method, please follow the rules below:

Grey background
Use the red PayMe logo on white or grey backgrounds.
Black background
Use the white PayMe logo on dark backgrounds.
Grey background
Use grey for consistency if that's how you display other methods.
Red background
Avoid using the logo anywhere there is a poor contrast with the background color.

 

Selecting PayMe as a payment option

 

Payment selection

Payment badges
Using payment badges or buttons
You may want to use clickable badges for a user to select their payment method, alternatively you can use our 'Pay with PayMe' button.


 

Radio buttons
Radio buttons
You can also display PayMe in a list of radio buttons.


 

Using a dropdown for payment options
Using a dropdown for payment options
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.

Caution Ensure that the payment option reads PayMe, no space. The P and M must be capitalised.


 

Checkout button
Checkout button
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:

 

Payme Logo red

Primary PayMe logo (Recommend)

 

Payme Logo black

Alternative PayMe logo

 

Payme Logo white

White PayMe logo

 

Payme Logo red with HSBC

Payme Logo red with HSBC

 

Download logos

Buttons

Depending on your site's colour scheme, we suggest using the primary PayMe button wherever possible.

Primary button

Payme button red

Colour variations

White
Payme button white

Dark alternative
Payme button black

Making a payment

On mobile

For checkout on mobile devices, users will be redirected to the PayMe app.

Suggested message overlay

We suggest displaying the following notification to illustrate the concept.

Info 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.

Sample mobile notification message

 

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:

Display PayCode within your checkout page.




 

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.

Or





 

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.

Display the PayCode on a new page.


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:

Download

 

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.


Payme paycode

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);
};

Download source code

 

© PayMe from HSBC | Terms & Conditions | Website Terms of Use | Privacy and Security
SVF License Number: SVFB002