Cashback (1.1)

Introduction

PayPay Open Payment API (OPA) is designed to be used by our payment partners to make payment-related operations in different scenarios, so as to deliver the best payment experience to end users. After being onboarded as a PayPay OPA client, depending on the contract, you will have the capability on one or more of the below:

  • Collect payment by directly debiting from PayPay user’s wallet
  • Use pre-authorization and capture payment flow to facilitate your purchase procedure
  • Easy web application integration with PayPay cashier page to collect payment
  • Build your own checkout experience with rich APIs provided by PayPay
  • Gratification Solution for granting Cashback to users

This document will be focusing on APIs that support the last solution to grant cashback to users.

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.

Acquire user authorization

To be able to grant cashback to a 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 Message
200 SUCCESS Success
202 REQUEST_ACCEPTED Means the request is received, and will be processed sometime later.
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 The set parameter is invalid.
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 A service error has occurred.
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.

Give Cashback to User

Status Code Message
400 CANCELED_USER Target user does not exist.
400 VALIDATION_FAILED_EXCEPTION Error handling request params.
400 FAILURE Duplicate transaction error.
401 USER_STATE_IS_NOT_ACTIVE The request cannot be accepted because the user status is Inactive.
404 FAILURE The transaction has failed.
500 UNAUTHORIZED_ACCESS Unauthorized access to resource server.

Check Cashback Details

Status Code Message
400 VALIDATION_FAILED_EXCEPTION Error handling request params.
400 TRANSACTION_NOT_FOUND Transaction not found.
500 UNAUTHORIZED_ACCESS Unauthorized access to resource server.

Reverse a given cashback

Status Code Message
400 VALIDATION_FAILED_EXCEPTION Error handling request params.
400 TRANSACTION_NOT_FOUND Transaction not found.
500 UNAUTHORIZED_ACCESS Unauthorized access to resource server.

Check Cashback Reversal Details

Status Code Message
400 VALIDATION_FAILED_EXCEPTION Error handling request params.
400 TRANSACTION_NOT_FOUND Transaction not found.
500 UNAUTHORIZED_ACCESS Unauthorized access to resource server.

Get user authorization status

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

Get masked user profile

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

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

Wallet

Everything related to user wallet balance

Give Cashback to User

Transfer money from merchants campaign wallet to user wallet.

Timeout: 30s

Request Body schema: application/json

CreateCashback

merchantCashbackId
required
string (MerchantCashbackId) <= 64 characters

The unique cashback 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

orderDescription
string <= 255 characters

Description of the order

walletType
string
Enum: "PREPAID" "CASHBACK"
expiryDate
string <date> (ExpiryDate)

The date on which the Cashback Expires at midnight

metadata
object

Extra information the merchant want to add

Responses

Request samples

Content type
application/json
{
  • "merchantCashbackId": "string",
  • "userAuthorizationId": "string",
  • "amount": {
    },
  • "requestedAt": 0,
  • "orderDescription": "string",
  • "walletType": "PREPAID",
  • "expiryDate": "2020-07-31",
  • "metadata": { }
}

Response samples

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

Check Cashback Details

Check the cashback details of the cashback given

Timeout: 10s

Responses

Response samples

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

Reverse a given cashback

Transfer money back from user wallet to merchants campaign wallet.

Timeout: 40s

Request Body schema: application/json

CreateCashbackReversal

merchantCashbackReversalId
required
string (MerchantCashbackReversalId) <= 64 characters

The unique cashback reversal transaction id provided by merchant

merchantCashbackId
required
string (MerchantCashbackId) <= 64 characters

The unique cashback transaction id provided by merchant

required
object (MoneyAmount)
requestedAt
required
integer (EpochTime)

Epoch timestamp in seconds

reason
string <= 255 characte