JSファンクションを使って機能を呼び出す場合、JWTを取得してペイロードがPayPayからのものであることを確認する必要があります。 JWTを確認する方法、Open Payment APIサービスの呼び出し回数を減らす方法、 およびPayPayからのペイロードであることを確認する方法について、手順を追って説明します。
PayPay OPAの利用を開始するには、事前に定められたプロセスに従ってPayPay加盟店の登録を行なってください。 このプロセスは 情報収集、手動検証、契約確認、およびクレデンシャル情報の発行から構成されます。
PayPayの加盟店として登録された後、以下の項目が設定されます。
これらの設定を管理するには、マーチャントパネルを使用するか、弊社営業担当までご連絡ください。
OPA API認証に関わることは全て API認証のページ にあります。
PayPay OPAはHTTPレスポンスステータスコードとOPAエラーコードを使用してリクエストの成功または失敗を示します。 これらの情報を基に、どのようなエラー対応をするべきかを判断できます。 通常、PayPay OPAは以下のHTTPレスポンスステータスコードを返します。
| Status | Code | Description |
|---|---|---|
| 200 | SUCCESS | Success |
| 400 | KID_NOT_FOUND | システムにKIDが存在しません。 |
| 401 | UNAUTHORIZED | 有効なAPI keyとsecretがセットされていません. |
| 429 | RATE_LIMIT | このステータスコードは、クライアントが一定期間内に送信したリクエストが多すぎたため、速度制限に達したことを示します。 リクエストの送信を遅くするか、上限引き上げをご要望の旨を弊社までご連絡ください。 |
| 500 | INTERNAL_SERVER_ERROR | この状態コードは、PayPay側で何か問題が発生したことを示します。 |
| 503 | MAINTENANCE_MODE | 定期メンテナンスです。 |
| 503 | SERVICE_UNDER_MAINTENANCE | 定期メンテナンスです。 |
基本的に、すべてのAPIレスポンスには X-REQUEST-ID がレスポンスヘッダーとして含まれます(一部例外を除きます)。
PayPayへお問い合わせの際は、このリクエストIDをご提示ください。
フォーマット: 英数字とハイフン(最大64文字)
例:
OPA45F681001AEF4605B2A50939F611F4B8
フロントエンドからの各レスポンスは、サーバーが取得した公開鍵/秘密鍵のランダムなペアを使用して暗号化されています。 JWTからバックエンドの応答を含むペイロードを取得できます。 JWTトークンを復号化するために、KIDを指定して公開鍵を取得するAPIを提供します。 KIDはレスポンスのJWTから取得できます。
KIDを取得したら、このドキュメントで説明している「Retrieves a public key given a KID」APIを呼び出して、KIDの公開鍵を取得します(セット返却します)。 公開鍵を取得したら、毎回get public key APIを呼び出す必要がないように、KIDと公開鍵をペアで保存してください。
注:これらのペアは常に日本標準時で毎週火曜日の15:00に更新されます。したがって、毎週火曜日の15:00に削除してください。
サーバーから受信したJWTを確認するには、まずjwtトークンからKIDを取得する必要があります。 たとえば、Javaでは次のようにしてKIDを取得できます。
final DecodedJWT decodedJWT = JWT.decode(payload);
final String kid = decodedJWT.getKeyId();
KIDを取得したら、公開鍵が保存されていない場合にのみ、 publicKey APIを呼び出します。
// setup...
final CacheServiceImplementation cacheServiceImplementation;
// ...
// inside some get public key method
String publicKey;
// we only call v1/publicKey if we don't know the public key.
if (cacheServiceImplementation.contains(KID)) {
// do not call publicKey api and instead retrieve it from the cache
publicKey = cacheServiceImplementation.get(KID)
}
else {
// call the publicKeyApi since we don't have the KID that contains a public key value
// curl example:
// curl "http://stg-api.paypay.ne.jp/v1/publicKey?kid=0b08710e-e8d6-4c4d-b46f-27509012ac21" \
// -H "Authorization: hmac OPA-Auth:a_1obUXXXXX_XXXX:BY8NnUuXXXXX_XXXXae8OXXXXX_XXXXP5Hhlx8+b0=:98aa04:1588839957:empty"
publicKey = Optional.ofNullable(restTemplate.exchange(
"http://stg-api.paypay.ne.jp/v1/publicKey/opa/api/v1/publicKey?kid={kid}", HttpMethod.GET,
new HttpEntity<>(headers),OPAPublicKey.class,kid,
)).map(OPAPublicKey::getData).map(OPAData::getPublicKey).orElseThrow(NoPublicKeyException::new);
// save the KID associated with the public and expire it next tuesday at 15:00.
cacheServiceImplementation.put(KID, public)
}
注:上記のcurlは例であり、マスクをしています。 実際はAPIキーとシークレットを使用してhmacを生成して設定する必要があります。
APIから下記のような応答が返されます。
{
"resultInfo": {
"code": "SUCCESS",
"message": "Success",
"codeId": "08100001"
},
"data": {
"publicKey": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi47XWBtFBi944WwTwkTm/yJeYqSXhKxnPMVa0M+PW7FbTqGQ2deeHAx9lC5MxkdnJfRPeOI+Zy7l9tJAN510gBin7QGH879B/5lxu887DuaN8ERn8T9g/2DXAzPq2roWCHtBkcfuHJtqBybVsBGZDrC54JutBDOl6ACnUYJbCIag6UBCwRkIxkA/9yRDGS6+uJkhpqPWFQdfwd8+8JP9D5c+dDEN9pwL6/X2kZkFKOdyheOiNrqjShokaNDYqu3vZbI40HI2b7SgTsF4elsyv5TBBjjTc9NfsLHELKEseCA9djDHNZksKHwU+fgXwY2bp+N/UdVW+BLqKNUDjkZGyQIDAQAB-----END PUBLIC KEY-----"
}
}
ペイロードを確認する方法の前に、JWTの構造を説明します。
{
"iss": "",
"exp": 1589531351,
"aud": "a_XXXXXXX",
"iat": 1589530451,
"payload": "{}"
}
audienceにはあなたのClientIdが設定されます。
expはJWTの有効期限です。
ペイロードはにはレスポンスの本文が設定されています。
次にペイロードを確認します。 まず、有効な公開鍵であることを確認するために以下を実行します。
final String yourPayload = ...
final String publicKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAi47XWBtFBi944WwTwkTm/yJeYqSXhKxnPMVa0M+PW7FbTqGQ2deeHAx9lC5MxkdnJfRPeOI+Zy7l9tJAN510gBin7QGH879B/5lxu887DuaN8ERn8T9g/2DXAzPq2roWCHtBkcfuHJtqBybVsBGZDrC54JutBDOl6ACnUYJbCIag6UBCwRkIxkA/9yRDGS6+uJkhpqPWFQdfwd8+8JP9D5c+dDEN9pwL6/X2kZkFKOdyheOiNrqjShokaNDYqu3vZbI40HI2b7SgTsF4elsyv5TBBjjTc9NfsLHELKEseCA9djDHNZksKHwU+fgXwY2bp+N/UdVW+BLqKNUDjkZGyQIDAQAB-----END PUBLIC KEY-----";
final String key = publicKey.substring("-----BEGIN PUBLIC KEY-----".length(), publicKey.indexOf("-----END PUBLIC KEY-----"));
byte[] decodedPublicKey = Base64.getDecoder().decode(key.getBytes(StandardCharsets.UTF_8));
X509EncodedKeySpec spec = new X509EncodedKeySpec(decodedPublicKey);
RSAPublicKey publicKey1 = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(spec);
Algorithm algorithm = Algorithm.RSA256(publicKey1, null);
String payload;
try {
// verify with the payload.
DecodedJWT decodedJWT = JWT.require(algorithm).build().verify(yourPayload);
String audClientId = decodedJWT.getClaims().get("aud")
if (!audClientId.equals("MY_CLIENT_ID")) {
throw new MismatchClientIdException();
}
payload = decodedJWT.getClaims().get("payload");
} catch (Exception e) {
// handle invalid signature or any other error.
}
ペイロードを取得したら、それをObjectまたはMapに変換し、データフィールド内のキー「responseValidTill」を確認する必要があります。
(例:payloadMap.get("data").get("responseValidTill") )
responseValidTill(エポック時間で表された時間)がレスポンスを受信した時刻よりも前の場合、このペイロードは無効となります。
注:JWTは常に作成後15分で期限切れになります。
KIDと公開鍵をともに保存して、OPAを毎回呼び出さないようにしてください。KIDは火曜日の15:00に新しく作成されるため、毎週取得する必要があります。
フロントエンドのJWTから返された署名付きレスポンスよりKIDを取得したら、このAPIを呼び出して公開鍵を取得する。
| kid required | string レスポンスから取得したKID |
{- "resultInfo": {
- "code": "string",
- "message": "string",
- "codeId": "string"
}, - "data": {
- "publicKey": "string"
}
}