【 Next 】PayPay の アプリコールを実装する

今や多くの日本人が当たり前に使っている PayPay ですが、API が公開されており web アプリに使用することが可能です。公式サイトにも js での実装方法が記載されていたので今回は Next を使って実装した方法を残しておきます。

アプリコールとは

PayPay で提供されている API の一つがアプリコールになります。

アプリケーション上でユーザーが購入のアクションをした際に PayPay のアプリが開かれてそこで決済が行われるパターンを実装するために提供されています。今回はこちらを使用したかったのでその流れを順を追って記録していきます。

参考: 公式サイト

前準備

PayPay の API を使用するためには各種キーが必要になってきますがそれらを取得するためにまず Developers アカウントをこちらから作成する必要があります。するとキーや ID などが割り当てられます。

次に開発にあたり普段お使いの PayPay アプリも開発者モードでサインインする必要があります。

PayPay アプリのサインイン画面で上部の PayPay のロゴを 7 回タップすると開発者モードのサインイン画面に遷移できるという隠しコマンド的な仕様でした。その画面で Developers アカウント作成時に発行されているいずれかの以下テストユーザーでログインする事が可能になっています。

また、API を使用するためには SDK のインストールが npm やら yarn やらで必要になってきますのでその辺りの追加も事前に行います。

// To install the client library using npm, execute the following
  npm i @paypayopa/paypayopa-sdk-node
  // To install the client library using yarn, execute the following
  yarn add @paypayopa/paypayopa-sdk-node

Create a QR Code

アプリコールを実装するにあたってまずこちらの Create a QR Code を実行する必要があります。アプリコールなので実際に QR の読み込みを行うようなことはしませんが仕様上 QR を create する必要があるということなのでしょうか。

以下が API 側の実装になります。

export type PayPayCreateQRResponse = {
  deeplink?: string;
  merchantPaymentId?: string;
  expiryDate?: number;
  errorMessage?: string;
};

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<PayPayCreateQRResponse>
) {
  ("use strict");
  const PAYPAY = require("@paypayopa/paypayopa-sdk-node");

  PAYPAY.Configure({
    clientId: process.env.API_KEY,
    clientSecret: process.env.API_SECRET,
    merchantId: process.env.MERCHANT_ID,
    productionMode: process.env.PRODUCTION_MODE === "true",
  });

  const { userAgent } = JSON.parse(req.body);

  const uuid = require("uuid");
  const merchantPaymentId = uuid.v4();

  let paypayPayload = {
    merchantPaymentId: merchantPaymentId,
    amount: {
      amount: 50,
      currency: "JPY",
    },
    codeType: "ORDER_QR",
    orderDescription: "test",
    isAuthorization: false,
    redirectUrl: "/redirect-url",
    redirectType: "WEB_LINK",
    userAgent: userAgent,
  };

  PAYPAY.QRCodeCreate(paypayPayload, (response: any) => {
    
    if (!response) {
      return res.status(400).json({
        errorMessage: "couldn't get response",
      });
    }

    if (!response.BODY) {
      return res.status(400).json({
        errorMessage: "no response BODY" + response.STATUS,
      });
    }

    return res.status(200).json({
      deeplink: response.BODY.data.deeplink,
      merchantPaymentId: response.BODY.data.merchantPaymentId,
      expiryDate: response.BODY.data.expiryDate,
    });
  });
}

Configure で指定している各種キーや ID はテストと本番で違うものが割り当てられているため環境変数として読み込む様にしてあります。

また、QRCodeCreate に渡す Payload についてですが、userAgent を指定してやる必要があるためクライアント側からのリクエストに userAgent を入れてもらう仕様にしました。その他 Payload でこちらが今回指定するものは merchantPaymentId, amount, redirectUrl などがあります。

merchantPaymentId は支払いの処理毎に発行する必要がある ID になっているため uuid を使用して生成しています。

amount では金額と通貨を指定します。今回は 50 “円" です。

redirectUrl では支払い後に自動的にリダイレクトされる先の url を指定してください。

クライアント側からは以下の様に呼び出しています。

const response = await fetch("/api/paypay/create", {
        method: "POST",
        body: JSON.stringify({
          userAgent: navigator.userAgent
        }),
      });
const data: PayPayCreateQRResponse = await response.json();

返ってくる deeplink を開くと PayPay が入っている端末ではアプリが開いてくれます。

今回はテストモードのため先ほどログインしたテストユーザを使えばユーザに擬似的にチャージされた残高を使って実際の支払いを行うことが可能です。

※ 何度か繰り返しテストで実行してみましたが何回かに一回 PayPay での支払い時にエラーが出てしまう現象がありました。連続で使うとその様なエラーが出る?様でしたが、時間をあけるとまた使用可能でした。

Get Payment Details

こちらの API は上記で生成した支払い処理のステータスを取得することができます。

その為公式ではこちらの API をクライアント側で 4 〜 5 秒間隔のポーリングで実装することを推奨している様です。ただ例えばリダイレクト先の画面の初期化時の処理で支払いステータスを確認する事もできると思います。支払い発生時の画面に PayPay アプリからリダイレクトなしでユーザが戻った場合は、その画面で何かユーザがアクションをした場合にこの API を呼ぶなどすれば同じく支払い完了のステータスをチェックすることは可能です。ただそこで何かしらのアクションを必要としてしまうためポーリングがよいとのことなのでしょう。そこはアプリ自体どのような仕様にしたいかによるかと思います。

今回 API 側は以下の様に実装しました。

export type PayPayGetPaymentDetailsResponse = {
  status: string;
};

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<PayPayGetPaymentDetailsResponse>
) {
  "use strict";
  const PAYPAY = require("@paypayopa/paypayopa-sdk-node");

  const { merchantPaymentId } = JSON.parse(req.body);

  PAYPAY.GetCodePaymentDetails(Array(merchantPaymentId), (response: any) => {
    return res.status(200).json({
      status: response.BODY.data?.status,
    });
  });
}

上で引数となっている merchantPaymentId は create で生成した支払いに対して一意の id になります。

レスポンスとして返されるステータスの詳細は公式の IF を参照という感じにしておきますが、とりあえず COMPLETED のステータスが返れば支払い完了なので購入完了のアクションを実行できるという感じです。

その他支払いキャンセルなどの API も用意されているのでそれらは詳細をご確認ください。

最後に

今回アプリコールを実装する大まかな流れを追っていきましたが比較的簡単に SDK を使って機能を呼び出せる点に使い勝手の良さを感じました。

本番環境で使うためには加盟店登録が必要で審査もある様です。

今回試しで使ってみましたが、審査内容にはその店の収益や販売形態なども記載する必要がありました。その為簡単なアプリに組み込んで使ってみるとかその様な使い方が許されるかどうかは微妙ですね。。今日本人にとって最も手軽な決算方法の一つのためそのようにフランクな使い方ができれば可能性も広がるかと思ったのですがどうなんでしょうか。

参考書籍