🏦

銀行振込の入金確認を自動化した話

2024/06/19に公開

はじめに

こんにちは。令和トラベルでバックエンドエンジニアをしている高橋です。
令和トラベルでは海外旅行予約アプリ「NEWT」を運営しています。
今日は、NEWTを銀行振込で予約した際の、入金の確認を自動化した話を書きたいと思います。

銀行振込の課題

NEWTでは海外旅行を予約する際の決済手段として、クレジットカードの他に銀行振込を用意しております。
海外旅行という高価な商品を販売する性質上、銀行振込という決済手段を用意することは、お客様の多様なニーズに応えるうえではなくてはなりません。
しかし、銀行振込での決済を用意したことで課題が発生してしまいました。

①お客様の手間が増える

銀行振込においては、どのお客様からの、どの予約に対する入金があったのかを特定できなければなりません。
そのため、お客様から当社の銀行口座に振込をしていただく際、振込人名義に氏名だけでなく予約番号を入力していただく必要がありました。

ただ、振込人名義に予約番号を入力し忘れるお客様もおり、確認のための連絡によりお客様の負担が増える場面がありました。

②社内の業務工数が増える

また、銀行振込においては社内の業務工数が増えるという課題もあります。
経理担当は、どの予約からの入金があったのかを定期的に確認し、予約の入金ステータスを更新したうえで、手配担当にフライトやホテルの手配開始を依頼しています。

また、入金金額が請求金額と一致しない場合には、カスタマーサポート担当からお客様に連絡し、返金や追加入金のご案内のやりとりをする必要があります。
予約件数が増えていく中で、このまま人力で入金の確認を行っていくのはいずれリソース的な限界が訪れるという危機感もありました。

このように銀行振込ではお客様、社内の双方に負担が生じていたため、上記の問題を解決する方法を考えました。

ソリューション

上記の問題を解決するために、私たちはGMOペイメントゲートウェイ社の提供する、PGマルチペイメントサービス内の銀行振込(バーチャル口座)機能を利用することにしました。

https://www.gmo-pg.com/service/mulpay/virtual/

銀行振込(バーチャル口座)の特徴は以下です。

  • APIを通して取引(予約)ごとに固有となるバーチャル口座(GMOあおぞらネット銀行)を作成することができる
  • バーチャル口座への入金があるとwebhookでの通知を受け取ることができる

バーチャル口座は予約ごとに固有の口座番号を持つため、どのお客様からどの予約に紐づく入金があったのかがシステム的に判別することができるようになります。
また入金のたびにwebhookの通知があるため、経理担当が定期的に入金の確認を行う必要がなくなります。

なおPGマルチペイメントサービスを利用するためには申し込みが必要です。
私たちはクレジットカード決済でもPGマルチペイメントサービスを利用していたので、そのまま銀行振込(バーチャル口座)も利用することができました。

実装手順

実装としては基本的にドキュメントに従って実装します。
※PGマルチペイメントサービスのAPIにはプロトコルタイプとOpenAPIタイプの2種類ありますが、OpenAPIタイプが最新かつ推奨されています。

①取引開始(バーチャル口座を作成)する

予約時に取引開始(バーチャル口座を作成)するには、支払い番号発行(/cash/charge)APIを利用します。
リクエストはこのような感じになります。

curl -X POST https://stg.openapi.mul-pay.jp/cash/charge \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: 9P8E5F20-7B03-4D9E-B2C3-E3DD8157F637" \
  -d '
{
  "merchant":
    {
      "name": "Test Shop",
      "nameKana": "テストショップ",
      "nameAlphabet": "Test Shop",
      "nameShort": "テスト",
      "contactName": "テスト企業",
      "contactEmail": "test@example.com",
      "contactPhone": "000-0000-0000",
      "contactOpeningHours": "10:00-19:00",
      "webhookUrl": "https://example.com/gmo_transfer_notification",
      "csrfToken": "xxxxxxxxxxxxxxxxxxx"
    },
  "order":
    {
      "orderId": "P-0000-0000",
      "amount": "10000",
      "currency": "JPY",
      "transactionType": "CIT"
    },
  "payer": { "name": "テスト 太郎" },
  "cashInformation": { "cashType": "BANK_TRANSFER_GMO_AOZORA" },
  "additionalOptions": { "accountHolderOptionalName": "P-0000-0000" }
}
     '

リクエストパラメータのポイントとしては以下です。

  • merchant.webhookUrl
    • バーチャル口座への入金があった際に通知先となるWebhook URL
    • merchant.csrfTokenと組み合わせて指定する
  • payer.email
    • 指定すると振込依頼メールがGMO PG社から自動的に送られる
    • 今回は自前実装の振込依頼メールを送りたいので指定しない
  • additionalOptions.accountHolderOptionalName
    • 指定するとバーチャル口座の名義に任意の文字列を付与することができる
    • 例. 「ニユートP-0000-0000」の「P-0000-0000」の部分

これで、予約時にバーチャル口座を作成することができるようになりました。

②取引停止(バーチャル口座を削除)する

予約キャンセル時に取引停止(バーチャル口座を削除)するには、取引キャンセル(/order/cancel)APIを利用します。
リクエストはこのような感じになります。

curl -X POST https://stg.openapi.mul-pay.jp/order/cancel \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: 52D79142-9D04-4AC8-B4F6-42A2CW96887A" \
  -d '{"accessId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}' 

リクエストパラメータのaccessIdは①取引開始(バーチャル口座を作成)するに成功すると返ってくる取引IDです。

③入金通知を受信する

最後に現金払い支払い完了通知を受信するためのwebhookのエンドポイントを実装します。
入金通知のリクエストボディからは入金額がわからないので、通知を受信→取引照会という流れで入金額を取得する必要があります。

取引照会するには、取引照会(/order/inquiry)APIを利用します。
リクエストはこのような感じになります。

curl -X POST https://stg.openapi.mul-pay.jp/order/inquiry \
  -H "Content-Type: application/json" \
  -H "Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Idempotency-Key: P0640C45-8B50-3589-989F-07D19202JA1D" \
  -d '{"accessId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}'

続いてWebhookを受信するためのエンドポイントを実装します。

// 説明のための擬似コード
app.post("/gmo_transfer_notification", async (req) => {
  // accessIdとcsrfTokenの一致するPaymentを取得する
  const payment = await getPaymentByAccessIdAndToken({
    accessId: req.body.accessId,
    csrfToken: req.body.csrfToken,
  })

  // 取引照会して入金額を取得する
  const order = await gmoClient.orderInquiry(payment.accessId);
  const depositAmount = order.cashResult.bankTransferPaymentInformation.depositAmount;

  if (depositAmount === payment.amount) {
    // 入金額が正しい場合の処理(入金ステータスの更新など)
  }

  if (depositAmount > payment.amount) {
    // 入金額が多い場合の処理(返金のご案内メール送信など)
  }

  if (depositAmount < payment.amount) {
    // 入金額が少ない場合の処理(追加振込の依頼メール送信など)
  }
);

入金通知ではaccessIdとcsrfTokenが渡ってくるので、それをもとにPayment(支払い)データを取得します。
その後、取引照会APIを呼ぶことで入金額を取得し、入金額に応じて処理を分岐させます。

このようにして、入金額が請求金額と一致する場合は、予約の入金ステータスを自動で更新することができるようになりました。
また入金額が不足している場合や過剰となっている場合も、お客様に自動でメールを送ることができるようになりました。

以上のように、銀行振込の入金確認を自動化することができました。

まとめ

PGマルチペイメントサービスの銀行振込(バーチャル口座)を使うことで、入金確認の自動化を実現することができました。
銀行振込での決済を導入したいけど、入金確認のために人手を使いたくない、そんな企業にとっておすすめできるサービスかと思います。
また既にPGマルチペイメントサービスのクレジットカード決済を利用している企業にとっては導入もスムーズです。

令和トラベル Tech Blog

Discussion