PreAuth & Capture (1.0)

Introduction

Many of our merchants need to block money in the users wallet which is captured later. These include Ondemand taxi's, ondemand services, ecommerce websites, etc. The goal here is to ensure the merchant can block the money at the time of creating an order and take the money at the time of fulfilling the order thereby reducing the risk in the system.

TLS implementation

The PayPay Open Payment API requires that you use TLS 1.2 or higher as a security measure. Note that you cannot connect with TLS1.0 and TLS1.1.

Onboard merchant

To start utilizing our Open Payment API platform, at first the business needs to be onboarded onboard as a PayPay merchant. This process usually consists of information collection, manual verification, contract confirmation and credentials issuance.

After becoming a merchant on PayPay, the following items would be setup for the client:

  • api key and secret
  • allowed authorization callback domains
  • user authorization validity time
  • webhook endpoints
  • client IP whitelist

This setup can be managed using our merchant panel/ getting in touch with the sales representative.

Access from users to the PayPay app and PayPay web screen from outside Japan is restricted. Please contact us for details.

Acquire user authorization

To be able to collect payment from PayPay user’s wallet, you need to obtain user’s authorization explicitly.

Acquiring user authorization can be achieved using our simple account linking flow here .

We also provided a web based account linking flow here , however the integration support for the same is not deprecated and we recommend you to try the simple account linking flow.

Api Authentication

All the following APIs need to be called via HMAC. For the implementation of HMAC based authentication, entire request is taken into account for building authentication object.

Parameters considered for authentication object

Value Description Example
API Key API key generated APIKeyGenerated
API Key secret API Key secret generated APIKeySecretGenerated
Request URI Service request URL /v2/codes
Request method HTTP method POST
Epoch Current epoch seconds,
Note : Validated whether less than 2mins on server side
1579843452
Nonce Random generated string
Note : String of any length, but recommended to use of length 8
acd028
Request body Value of request body passed in request

{"sampleRequestBodyKey1":"sampleRequestBodyValue1","sampleRequestBodyKey2":"sampleRequestBodyValue2"}

Request content type Content type passed in request header application/json;charset=UTF-8;
hash (MD5(Request body, Request content type)) Hash of request body and content type. This will be the output from step 1 of genererating HMAC auth header added below 1j0FnY4flNp5CtIKa7x9MQ==

Sample HMAC Auth header generated from above example values is as followed

hmac OPA-Auth:APIKeyGenerated:NW1jKIMnzR7tEhMWtcJcaef+nFVBt7jjAGcVuxHhchc=:acd028:1579843452:1j0FnY4flNp5CtIKa7x9MQ==

Steps to genererate HMAC auth header

Step 1: Hash the body and content-type with MD5 algorithm

Code sample for hashing body and content type

private String requestBody;
private String contentType;

MessageDigest md = MessageDigest.getInstance("MD5");
md.update(contentType.getBytes(StandardCharsets.UTF_8));
md.update(requestBody.getBytes(StandardCharsets.UTF_8));
String hash = new String(
          Base64.getEncoder().encode(md.digest()),
          StandardCharsets.UTF_8);

Note : If there is no request body, for instance, the HTTP GET method case, no need of generating MD5. Instead hash value is set as "empty".

Step 2 : Build string to be hashed with HMAC-SHA256.

We will consider following parameters for signing the request

private String requestUrl; //Only the request URI Example: "/v2/codes/payments/dynamic-qr-test-00002"
private String httpMethod;
private String nonce; //Random string
private String epoch;
private String contentType;
private String hash; //Output of step 1
private static final DELIMITER = "\n";

byte[] hmacData = new StringBuilder()
                    .append(path)
                    .append(DELIMITER)
                    .append(method)
                    .append(DELIMITER)
                    .append(nonce)
                    .append(DELIMITER)
                    .append(timestamp)
                    .append(DELIMITER)
                    .append(contentType)
                    .append(DELIMITER)
                    .append(hash != null ? hash : "")
                    .toString()
                    .getBytes(StandardCharsets.UTF_8);

Note : If there is no request body, for instance, the HTTP GET method case, content-type and hash will be set as "empty".

Step 3 : Generating HMAC object Generate HMAC based on output from Step 2 and Key secret of API key secret pair.

public final String toBase64HmacString() {
    private String apiKeySecret;
    private byte[] dataToSign; //Output from step 2
    try {
      SecretKeySpec signingKey = new SecretKeySpec(apiKeySecret.getBytes(StandardCharsets.UTF_8),
          "HmacSHA256");
      Mac sha256HMAC = Mac.getInstance("HmacSHA256");
      sha256HMAC.init(signingKey);
      byte[] rawHmac = sha256HMAC.doFinal(dataToSign);
      return java.util.Base64.getEncoder().encodeToString(rawHmac);
    } catch (GeneralSecurityException e) {
      LOGGER.error("Unexpected error while creating hash: " + e.getMessage());
      throw new IllegalArgumentException(e);
    }
  }

Step 4 : Build header object with the parameters of syntax. //We will be using password as the key to sign the hmacData

String authHeader = "hmac OPA-Auth:" + api-key +
":" + macData + ":" + nonce + ":" + epoch + ":" + hash;

Note: macData is output from step 3 and
For nonce and epoch, same value that has been used in step 2 should be passed.

If there is no body of the request such as in the case of HTTP GET method, the hash value will be "empty", so the header object will be as follows.

String authHeader = "hmac OPA-Auth:" + api-key +
":" + macData + ":" + nonce + ":" + epoch + ":empty";

The value of authHeader is passed in HttpHeader.AUTHORIZATION. With the authHeader will decode back the data added and with the HTTP request object and based on data available for api-key in the system, we will recreate the SHA256("key", requestParams) which gives macData. This macData is verified against the value passed in the header.

Specify merchant in request

As an agent client may link to mulitple merchants, everytime calling an api, the merchant identifier need to be passed along with the request. There are two ways to pass the merchant identifier: In query string:

assumeMerchant=shop

Or in http headers:

X-ASSUME-MERCHANT: auction

If both are provided, the query string parameter would take precedence.

Error Handling

PayPay OPA uses HTTP response status codes and OPA error code to indicate the success or failure of the requests. With these information, you can decide what error handling strategy to use. In general, PayPay OPA return the following http status codes.

Response code list

Common response code

Status Code Description
200 SUCCESS Success
400 INVALID_REQUEST_PARAMS The infomation provide by the request contains invalid data. E.g. unsupported currency
401 OP_OUT_OF_SCOPE The operation is not permitted
400 MISSING_REQUEST_PARAMS
401 UNAUTHORIZED No valid api key and secret provided
404 OPA_CLIENT_NOT_FOUND OPA Client not found
429 RATE_LIMIT Too many requests.
500 SERVICE_ERROR
500 INTERNAL_SERVER_ERROR This code means that something goes wrong, but we don't konw exactly if the transaction has happened or not. It should be treatd as unknown payment status
503 MAINTENANCE_MODE Sorry, we are down for scheduled maintenance.

Create a payment authorization

Status Code Description
400 CANCELED_USER Canceled user
400 INVALID_PARAMS Invalid parameters received
400 NO_SUFFICIENT_FUND The user's balance is insufficient to make payment.
400 UNSUPPORTED_PAYMENT_METHOD Payment method is not supported
400 PRE_AUTH_CAPTURE_UNSUPPORTED_MERCHANT Merchant do not support Pre-Auth-Capture
400 PRE_AUTH_CAPTURE_INVALID_EXPIRY_DATE Provided Expiry Date is above the allowed limit of Max allowed expiry days
400 SUSPECTED_DUPLICATE_PAYMENT

If a merchant tries collect same amount money from same user again within 5 minutes, the request would be rejected with this very error code. This design is mainly to prevent the duplicated payments which are usually caused by design flaws in client code.

However, sometimes, the merchant would intentionally collect mutiple same amount payments from a single user. In such case, the client need to send a specific parameter in order to bypass the duplication check. This is detailed in the payment creation api spec.

400 UNACCEPTABLE_OP The requested operation is not able to be processed due to the current condition. E.g. the transaction limit exceeded
401 USER_STATE_IS_NOT_ACTIVE Inactive user
401 INVALID_USER_AUTHORIZATION_ID If the user authorization id is expired or revoked by the use. The client need to go through the authorization flow again to get the user authorization id
401 EXPIRED_USER_AUTHORIZATION_ID The user authorization ID expired
404 NO_VALID_PAYMENT_METHOD No available payment method
404 PAYMENT_METHOD_NOT_FOUND Payment method not found
500 TRANSACTION_FAILED This code means the transaciton is failed in PayPay side. You can create new transaction for the same purpose with reasonable backoff time

Cancel a payment authorization

Status Code Description
400 ORDER_NOT_REVERSIBLE This code will be returned if the status of the target order is other than "AUTHORIZED". Revert cannot be performed because the status is not "AUTHORIZED".

Capture a payment authorization

Status Code Description
202 USER_CONFIRMATION_REQUIRED User confirmation required as requested amount is above allowed limit
400 ORDER_NOT_CAPTURABLE Order is not capturable.
400 CANCELED_USER Canceled user
400 INVALID_PARAMS Invalid parameters received
400 NO_SUFFICIENT_FUND The user's balance is insufficient to make payment.
400 ORDER_EXPIRED Order cannot be captured or updated as it has already expired.
400 REAUTHORIZATION_IN_PROGRESS Order is being reauthorized
400 ALREADY_CAPTURED Cannot capture already captured acquiring order.
400 TOO_CLOSE_TO_EXPIRY Order cannot be reauthorized as request is too close to expiry time
400 UNACCEPTABLE_OP The requested operation is not able to be processed due to the current condition. E.g. the transaction limit exceeded
401 USER_STATE_IS_NOT_ACTIVE Inactive user
404 RESOURCE_NOT_FOUND Order not found
500 BACKEND_TIMEOUT Timeout occurred while accessing external service

Revert a payment authorization

Status Code Description
400 INVALID_PARAMS Invalid parameters received
400 ORDER_NOT_CANCELABLE Order is not cancelable.
404 RESOURCE_NOT_FOUND Order not found

Refund a payment

Status Code Description
400 INVALID_PARAMS Invalid parameters received
400 UNACCEPTABLE_OP Order cannot be refunded
400 CANCELED_USER Target user does not exist.
401 USER_STATE_IS_NOT_ACTIVE The request cannot be accepted because the user status is Inactive.
404 NO_SUCH_REFUND_ORDER The specified refund payment could not be found.
404 RESOURCE_NOT_FOUND Order not found

Get refund details

Status Code Description
404 NO_SUCH_REFUND_ORDER Refund not found

Create a topup QR Code

Status Code Description
400 DUPLICATE_TOPUP_QR_REQUEST Topup QR with same merchantTopUpId exists.

Get topup status

Status Code Description
404 TOPUP_DETAILS_NOT_FOUND Invalid merchantTopUpId.

Delete QR code

Status Code Description
404 TOPUP_QR_CODE_NOT_FOUND Invalid QR codeId.
400 TOPUP_ALREADY_DONE The topup has been done for given qrCodeId.
Status Code Description
401 INVALID_USER_AUTHORIZATION_ID The specified user authorization ID is invalid.

Get user authorization status

Status Code Description
401 INVALID_USER_AUTHORIZATION_ID The specified user authorization ID is invalid.

Get masked user profile

Status Code Description
401 INVALID_USER_AUTHORIZATION_ID The specified user authorization ID is invalid.

Check user wallet balance

Status Code Description
400 CANCELED_USER Canceled user

Timeout

The recommended timeout setting is specified in each API. The most important one is for the payment creation api, where the read timeout should not be less than 30 seconds. When timeout happens, it should be treated as unknown payment status.

Handle unknown payment status

There are two ways to react with this situation

  1. Use the query api to query the transaction status. If the original transaction was failed or not found in PayPay, you can start a new transaction for the same purpose.

  2. Or, you can cancel the transaction, if the cancel api is provided. After the cancellation is accepted, you can start a new transaction for the same purpose.

Please check here for additional FAQs for OPA

Screen transision diagram

This screen transition diagram describes the display of the application after the purchase operation.

Mapping of API Request Parameter and display screen are shown in the here

Display of the application after payment are shown in the here

Payment

Everything involved payment life cycle

Create a payment authorization

Create a payment authorization to block the money

Timeout: 30s

query Parameters
agreeSimilarTransaction
string

(Optional) If the parameter is set to "true", the payment duplication check will be bypassed.

Request Body schema: application/json

Payment

merchantPaymentId
required
string (MerchantPaymentId) <= 64 characters

The unique payment transaction id provided by merchant

userAuthorizationId
required
string (UserAuthorizationId) <= 64 characters

The PayPay user reference id returned by the user authorization flow

required
object (MoneyAmount)
requestedAt
required
integer (EpochTime)

Epoch timestamp in seconds

expiresAt
integer (EpochTime)

Epoch timestamp in seconds, expiry duration must be less then the expiry granted to the merchant

storeId
string <= 255 characters

Id to identify store under merchant

terminalId
string <= 255 characters

Id to identify terminal device under store

orderReceiptNumber
string <= 255 characters

Receipt number provided by merchant

orderDescription
string <= 255 characters

Description of the order

Array of objects (MerchantOrderItem)
metadata
object

Extra information the merchant want to add

Responses

Request samples

Content type
application/json
{
  • "merchantPaymentId": "string",
  • "userAuthorizationId": "string",
  • "amount":
    {
    },
  • "requestedAt": 0,
  • "expiresAt": null,
  • "storeId": "string",
  • "terminalId": "string",
  • "orderReceiptNumber": "string",
  • "orderDescription": "string",
  • "orderItems":
    [
    ],
  • "metadata": { }
}

Response samples

Content type
application/json
{
  • "resultInfo":
    {
    },
  • "data":
    {
    }