【シリーズリンク】
#内容
1PayPay for Developers へサインアップ
2PayPay API について ←今回
3サンプルアプリケーション
4加盟店登録


前回の続きです。今回はウェブアプリに PayPay 決済機能を実装するための API(SDK) と、この API をどう使って(次回紹介予定の)サンプルウェブアプリケーションを実装したか、を紹介します。

前回紹介した PayPay for Developers にアカウント登録をすることで PayPay API を使ったアプリケーション開発の準備ができました。この後、実際に動くサンプルアプリケーションも紹介予定ですが、そのサンプルアプリケーションの中で使っている PayPay 決済機能がどのような API を使ってどのように実装されているか、を紹介します。


【PayPay 公式 API】
まず PayPay が公開している公式 API であるインテグレーション SDK はこちらです:
https://developer.paypay.ne.jp/products/docs

同ページ内で様々な SDK が紹介されていますが、特にウェブアプリケーションの中で決済を実施するためのウェブペイメント SDK についての公式資料はこちらです:
https://developer.paypay.ne.jp/products/docs/webpayment

このブログシリーズの次回でサンプルアプリケーションを紹介しますが、そのサンプルアプリケーションは Node.js を使って実装しています。この Node.js 用 SDK の公式資料はこちらです(本ブログエントリで紹介する内容はこのページで紹介されているものの抜粋です):
https://www.npmjs.com/package/@paypayopa/paypayopa-sdk-node/v/0.5.1


自作サービスで PayPay を有効にするには上記の API(SDK) を使って決済したり、決済状況を確認したり、といった機能をアプリケーション内で実装する必要があります。以下では利用場面別に3つのユースケースと、そのための API(SDK) 実装方法を紹介します。


【データベースの用意】
まず最初にデータベースを用意します。詳しくは後述しますが、PayPay は単に支払いに使うだけなら決済情報を記録する必要はないのですが、実際に運用する場合は決済情報を記録しておかないと(返金リクエストなどの)各種問い合わせに対応できなくなってしまいます。

以下の説明ではこのような定義の transactions テーブルが作成されていることを前提に説明しています。もちろん利用目的によってはユーザーの情報などを加えてもいいと思いますが、最低限このくらいの情報を記録しておく必要がある、と思ってください:
create table if not exists transactions ( id varchar(100) not null primary key, order_id varchar(50) not null, amount int default 0, currency varchar(10) default '', created bigint default 0 );

この transactions テーブル内の各列には以下のような情報を格納します:
列名格納する値
idID(merchantPaymentId)
order_idオーダー ID(利用者からは「取引番号」として見える値)
amount取引額
currency取引通貨
created作成日時のタイムスタンプ



【SDK の初期化】
次に以下で3つ紹介するユースケースの全てで共通しているのですが、Node.js 用 PayPay SDK を使う場合、各処理を実行する前に SDK を初期化しておく必要があり、この初期化の際には以下3つの値が必要です:
・API キー
・API シークレット
・加盟店 ID

これらは PayPay for Developers にログインした直後のダッシュボード画面に表示されているものを使います(他の人から教えてもらったり、教えたりするものではないので注意してください):
2025052910


これらをの値を使って、以下のコードを実行して初期化を実行しておきます:
var PAYPAY = require( "@paypayopa/paypayopa-sdk-node" );
var PAYPAY_API_KEY = '(API キー)';
var PAYPAY_API_SECRET = '(API シークレット)';
var PAYPAY_MERCHANT_ID = '(加盟店 ID)';

PAYPAY.Configure({
  clientId: PAYPAY_API_KEY,
  clientSecret: PAYPAY_API_SECRET,
  merchantId: PAYPAY_MERCHANT_ID,
  productionMode: false  //. テスト環境の場合は false 、本番の場合は true
});

以下3つのユースケース全てはここまで完了している前提で実行してください。


【ユースケース1 ウェブ決済】
ある意味最もシンプルな「PayPay アプリで支払いを行う」場面を想定した機能を作る部分です。支払いを行う利用者の立場で考えると「QR コードを読み取って支払う」という1ステップだけの機能だけのように感じられるかもしれませんが、ここでは以下のような3ステップを実行します:

(1)支払い内容に応じた QR コード(の URL )を作る
(2)(1)で作った URL にユーザーを転送させて QR コード決済してもらう
(3)決済内容を(返金対応ができるように)記録する

(1)ではサーバーサイドで以下のようなコードを実行します(◎◎シャンプーという 1000 円の商品を購入した例での内容です):
var { v4: uuidv4 } = require( 'uuid' );  //. UUID 作成用
  :
  :

    var payload = {
      merchantPaymentId: 'paypayapi-sample' + uuidv4(),  //. ユニークID
      amount: { amount: '1000', currency: 'JPY' },  //. 1000円
      codeType: "ORDER_QR",
      orderDescription: '◎◎シャンプー',
      isAuthorization: false,
      redirectUrl: 'http://localhost:8080/paypay/redirect',  //. リダイレクト先
      redirectType: "WEB_LINK",
      userAgent: 'PayPayAPI Sample App/0.0.1'
    };

    //. 決済用 QR コードページを生成
    PAYPAY.QRCodeCreate( payload, function( response ){
      if( response.STATUS == 201 ){
        //. 決済内容をセッションに記録
        req.session.qr_data = response.BODY.data;

        //. 生成された QR コードページにリダイレクト
        res.redirect( response.BODY.data.url );
      }


ここでは QR コードページを生成するために PAYPAY.QRCodeCreate という関数を実行するのですが、その関数に渡すパラメータオブジェクトである payload という変数を定義します。ここでは merchantPaymentId にユニークな ID を生成して入れておきます。また amount には決済額の情報を数値と通貨単位に分けて入力し、orderDescription に決済内容を示す文字列(商品名など)、そして redirectUrl に(3)での処理を想定した QR コード決済が完了した後に戻ってくるページ(リダイレクト先)を指定します。また codeType には "ORDER_QR" を、redirectType には "WEB_LINK" をそれぞれ指定します。

このようにして作成した payload を引数指定して PAYPAY.QRCodeCreate を実行します。そして生成が成功したこと(response.STATUS が 201 になっていること)を確認後に(決済後のリダイレクト先で再度この時の情報にアクセスできるように)決済内容をセッションに記録した上で生成された QR コードページに利用者をリダイレクトする、という処理を実行しています。

(2)利用者は(1)の最後のリダイレクト先の QR コードを PAYPAY アプリで読み取って決済します。決済が完了すると利用者は(1)の payload.redirectUrl で指定したリダイレクト先に転送されます。

(3)利用者の決済が完了した後に転送された後の処理です。この時点で決済は完了しているので、支払い自体は済んでいるとも言えます。ただここで決済の情報を記録しておかないと、誰がいつどんな決済を行ったのか、という情報は PayPay for Developers のダッシュボード画面を見ないとわからなくなってしまいますし、あるユーザーから(クーリングオフなどの理由で)返金を求められた場合、いつのどの決済に対する返金対応なのかもわからなくなってしまいます。その対応のためにも完了した決済内容はデータベースなどに記録しておくことが必要だと思っています((1)で決済直前に決済内容をセッションに記録していたのはそのためです)。

そのための実装は以下のようになります:
//. 決済完了後のリダイレクト先での処理
app.get( '/paypay/redirect', async function( req, res ){
  //. (1)で記憶させた内容をセッションから取り出し
  var qr_data = req.session.qr_data;

if( qr_data.merchantPaymentId ){ //. merchantPaymentId を使ってオーダー ID を取り出す var order_id = ''; var response = await PAYPAY.GetCodePaymentDetails( Array( qr_data.merchantPaymentId ) ); if( response.STATUS == 201 ){ var body = JSON.parse( response.BODY ); //. body.data.paymentId に格納されたオーダー ID を取り出す if( body && body.data && body.data.paymentId ){ order_id = body.data.paymentId; } } //. qr_data.merchantPaymentId, order_id, qr_data.amount.amount, qr_data.amount.currency をデータベースに記録する : :

まず(1)で指定したリダイレクト先 URL である '/paypay/redirect' での処理内容として以下を記述します。最初に(1)でセッションに記憶させた情報を取り出し、(1)実行時に生成したユニーク ID である merchantPaymentId を引数に PAYPAY.GetCodePaymentDetails 関数を実行して決済結果の状況を取り出します。

データベースに記録しておきたい情報は「利用者側からの決済情報 ID として確認できる オーダー ID 」の値で、利用者の PAYPAY 画面からは取引履歴内の「詳細を見る」という部分を展開した先の「取引番号」として表示されているものです:
2025060901


※もし利用者から返金対応を求められた場合は、利用者にこの画面を出してもらい、「取引番号を教えてください」と伝えて教えてもらうことで、サービス運用側からもどの時の決済に対する返金対応なのか、を特定することができるようになります。そのためにも(3)の処理の一部として、決済情報をデータベースに記録しておくことが必要です。


このオーダー ID の値は PAYPAY.GetCodePaymentDetails 実行後の body.data.paymentId に格納されているので、この値を取り出します。

最終的には
・実行時に生成したユニーク ID (merchantPaymentId)
・利用者側からの決済情報として確認できるオーダー ID(order_id)
・決済額と決済通貨単位(amount, currency)
・その他、日付情報など
を別途データベースに格納しておくことで、ユーザーからの決済に関する問い合わせにも対応ができるようになる、というものです。

ユースケース1においては単なる支払いだけなら(1)と(2)の処理だけ実装すればできますが、実際に運用することまで考慮すると(3)の処理も事実上必須、だと思っています。


【ユースケース2 ウェブ決済状況確認】
「PayPay アプリで支払い」の機能さえあればよいかというと、そうはいきません。そもそも支払いは完了したのかしていないのか、支払いを行った結果どうなったか、返金対応は完了しているのか、といった状況を確認できるようにしておく必要もあります。後述の返金対応をする上でも確実に支払われたと分かっている取引だけに対応する必要があるため、支払い結果を確認することができる機能も必要です。

この機能はユースケース1の(3)でも利用した PAYPAY.GetCodePaymentDetails 関数を使って実装します。状況を確認したい取引の merchantPaymentId を指定して、以下のように実行します:
PAYPAY.GetCodePaymentDetails( Array( merchantPaymentId ), function( response ){
  if( response.STATUS == 201 ){
    //. 取引情報の取得に成功
      :
      :


なお、取引情報のステータスを示す値は response.BODY.data.status に文字列として格納されています。取り得る値とそのステータスは以下のようになります(ウェブペイメントで目にする頻度が高いのは緑のステータスです):
ステータス
CREATED受付済み(QRコード作成済み)
AUTHORIZED残高ブロック済み
REAUTHORIZED増額キャプチャ時のユーザー承認後処理中
COMPLETED取引完了(支払い済み)
REFUNDED返金済み
FAILED取引失敗
CANCELED残高ブロック中止(キャンセル
EXPIRED残高ブロック期限切れ


この関数によって、merchantPaymentId が分かっていればその取引の決済状況を確認することができるようになります。


【ユースケース3 返金対応】
もう1点、上述でも何度か触れていますが、返金にも対応できるようにしておく必要があります。PayPay で決済可能なサービスを作っている以上、クーリングオフなど購入者側の事情による返金を考慮しないわけにはいきません。「返金には対応しない」わけにはいかない以上、返金要望にも対応できるようなアプリケーションにしておく必要がある、というものです。

利用者からの返金リクエストを受ける場合、まず利用者に対象取引の「取引番号」を調べてもらう必要があります。取引番号は PAYPAY アプリの取引履歴から返金対象の決済を選んだあとに「詳細を見る」を選択後に表示されます。まずはこの取引番号を利用者から教えてもらいます:
2025060901


取引番号(データベース上ではオーダー ID)がわかったら、その取引番号をオーダー ID として持つレコードを検索します(上述の transactions テーブルを使い、該当するオーダー ID を持つレコードの id(=merchantPaymentId), amount, currency を検索します。

返金対応したい決済の内容が分かったら、返金理由などと併せて PAYPAY.PaymentRefund 関数を実行して返金処理を実施します(以下のコードはデータベースに PostgreSQL を使う想定で記述されています):
var PG = require( 'pg' );
var database_url = 'posgtres://user:pass@server:5432/db';
var pg = new PG.pool( { connectionString: database_url } );
var conn = await pg.connect();

var order_id = '(返金対応したいレコードの order_id)';

//. order_id に対応する id や返金額をデータベースから取り出す
var sql = "select id, amount, currency from transactions where order_id = $1";
var query = { text: sql, values: [ order_id ] };

var result = await conn.query( query );
var id = result.rows[0].id;  //. 返金対応したいレコードの id
var amount = parseInt( result.rows[0].amount );
var currency = result.rows[0].currency;

//. 該当レコードの決済から返金する
var payload = {
  merchantRefundId: id + '-refund',
  paymentId: order_id,
  amount: { amount: amount, currency: currency },
  reason: 'クーリングオフ'  //. 返金理由
};
PAYPAY.PaymentRefund( payload, function( response ){
  if( resopnse.STATUS == 201 ){
    //. 返金成功
      :
      :


以上、今回は Node.js 用の PAYPAY API(SDK) を使って PayPay での決済や状況確認、返金処理といった実践的な処理を実装するための前提条件や方法をざっと紹介しました。

次回は今回紹介した内容を使って作成したサンプルアプリケーションをテスト環境で動かし、PayPay アプリでの決済や返金処理といった内容を実際に体験する予定です。