Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
193
Help us understand the problem. What are the problem?

posted at

updated at

PayPal 決済の実装方法

PayPalアカウント登録・API認証情報取得

PayPalアカウント登録・API認証情報取得

PayPal決済実装方法

REST API URL

PayPal REST APIと通信するには、サンドボックス環境または本番環境のいずれかで、JSON形式のHTTPリクエストをAPIエンドポイントに渡します。

環境 エンドポイント
Sandbox https://api-m.sandbox.paypal.com
本番環境 https://api-m.paypal.com

APIべき等性

API idempotency

サーバーがリソースに対するアクションを複数回作成したり完了したりすることを気にすることなく、何度でもコールを行うことができます。ネットワークタイムアウトや HTTP 5xx ステータスコードで失敗した呼び出しを、サーバが ID を保存している限り再試行することができます。べき等性を使用すると、リクエストのペイロードをレスポンスのペイロードと相関させ、重複したリクエストを排除し、失敗したリクエストやレスポンスが不明瞭なリクエストを再試行することができます。

REST APIのPOST呼び出しでIdempotencyを強制するには、PayPal-Request-Idリクエストヘッダーを使用します。このヘッダーには、サーバーが一定期間保存する一意のユーザー生成IDが含まれています。

注意 すべてのAPIがこのヘッダーをサポートしているわけではありません。お使いのAPIがこのヘッダーをサポートしているかどうか、およびサーバーがIDを保存する期間に関する情報については、お使いのAPIのリファレンスを参照してください。

たとえば、以前に指定したPayPal-Request-Idヘッダーをリクエストに含めると、PayPalは、同じヘッダーを使用した以前のリクエストの最新のステータスを返します。逆に、リクエストからPayPal-Request-Idヘッダーを省略すると、PayPalはリクエストを複製します。

注意 同じPayPal-Request-Idヘッダーを持つ2つのAPIリクエストを同時に送信すると、PayPalは最初のリクエストを処理し、2つ目のリクエストに失敗する可能性があります。

使用上の注意

  • PayPal-Request-Idヘッダー値は、各リクエストとAPIコールタイプの両方で一意でなければなりません。たとえば、authorize paymentやcapture authorized paymentなどです。
  • PayPalは、元のリクエストのステータスではなく、現在の時点でのリクエストのステータスを提供します。

JavaScript SDK

JavaScript SDKは、あなたのページにPayPalがサポートしている支払い方法を表示し、バイヤーにパーソナライズされた合理的なチェックアウト体験を提供します。

JavaScript SDK スクリプトにクエリとスクリプトのパラメータを渡すことで、実装をカスタマイズできます。これらのパラメータは、PayPal が最適な決済方法とボタンを決定して、バイヤーに表示するのに役立ちます。

JavaScript SDK script configuration
JavaScript SDK Complete Reference
JavaScript SDK performance optimization

PayPal決済

概要

  1. 以下のサンプルコードをコピーして、チェックアウトページに貼り付けてください。
  2. コード中の YOUR_SB_CLIENT_ID を、使用するClient IDに置き換えてください。
  3. 必要に応じてJavaScript SDKに渡すパラメータを明示的に指定して、デフォルトの値を上書きしてください。各パラメータのデフォルト値についてはJavaScript SDK script configurationを参照してください。
  4. 決済情報を送信するためのサーバ側コードを実装してください。createOrderとonApproveのメソッド内からサーバ側へリクエストしてください。サーバ側の実装は以降の説明を参照してください。

Access Tokenを作成する

Exchange your API credentials for an access token

サンプルリクエスト
curl -v https://api-m.sandbox.paypal.com/v1/oauth2/token \
-H "Accept: application/json" \
-H "Accept-Language: en_US" \
-u "<client_id>:<secret>" \
-d "grant_type=client_credentials"

サンプルレスポンス
{
    "access_token": "A21AAGk9mYWDNvy-OzYmU985kgwscf16by3R_97_RF-t90y_0TFTFQIhSndxdUcpfuY9dnMOpbwuNkmOZrsfnlmVSIPIOGraQ",
    "app_id": "APP-804485P543195W28T",
    "expires_in": 32400,
    "nonce": "2019-12-04T09:29:10Z01fPaJg9ztZoKhcsgIG1cjlWPxTy_HCiHPIDtPKvfDs",
    "scope": "https://uri.paypal.com/services/invoicing https://uri.paypal.com/services/disputes/read-buyer https://uri.paypal.com/services/payments/realtimepayment https://uri.paypal.com/services/disputes/update-seller https://uri.paypal.com/services/payments/payment/authcapture openid https://uri.paypal.com/services/disputes/read-seller https://uri.paypal.com/services/payments/refund https://api.paypal.com/v1/vault/credit-card https://api.paypal.com/v1/payments/.* https://uri.paypal.com/payments/payouts https://api.paypal.com/v1/vault/credit-card/.* https://uri.paypal.com/services/subscriptions https://uri.paypal.com/services/applications/webhooks",
    "token_type": "Bearer"
}

PayPal決済ボタンを追加する

Set up standard payments

PayPal JavaScript SDKのデフォルトのintentは、指定しない場合intent=captureであり、トランザクションのオーソリ(authorize)と売上(capture)の両方を直ちに行うことです。オーソリと売上を別々のアクションに分割するには、PayPal JavaScript SDK のスクリプトタグにintent=authorize を追加します。

PayPal決済ボタンのカスタマイズは Style guide を参照してください。

サンプルフォーム
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Ensures optimal rendering on mobile devices. -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <!-- Optimal Internet Explorer compatibility -->
  </head>

  <body>
    <script src="https://www.paypal.com/sdk/js?client-id=YOUR_SB_CLIENT_ID &locale=ja_JP&currency=JPY&disable-funding=card&intent=capture"> // Replace YOUR_SB_CLIENT_ID with your sandbox client ID
    </script>

    <div id="paypal-button-container"></div>

    <!-- Add the checkout buttons, set up the order and approve the order -->
    <script>
      paypal.Buttons({

        createOrder: function() {
        return fetch('/my-server/create-paypal-transaction', {
            method: 'post',
            headers: {
            'content-type': 'application/json'
            }
        }).then(function(res) {
            return res.json();
        }).then(function(data) {
            return data.orderID; // Use the same key name for order ID on the client and server
        });
        },
        onApprove: function(data) {
        return fetch('/my-server/capture-paypal-transaction', {
            headers: {
            'content-type': 'application/json'
            },
            body: JSON.stringify({
            orderID: data.orderID
            })
        }).then(function(res) {
            return res.json();
        }).then(function(details) {
            alert('Transaction funds captured from ' + details.payer_given_name);
        })
        }
      }).render('#paypal-button-container'); // Display payment options on your web page
    </script>
  </body>
</html>

決済を作成する

サンプルHTMLフォームの /my-server/create-order で下記 /v2/checkout/orders を貴社サーバからコールし決済を作成します。

Create an order
https://developer.paypal.com/docs/api/orders/v2/#orders_create

IntentにCAPTUREまたはAUTHORIZEを設定します。JavaScriptのクエリパラメータで指定したintentと一致する必要があります。intent パラメータをCaptureにすると決済が確定されます。Authorizeの場合には、与信のみを取得して決済の確定は行われません。

以下のパラメータを追加してください。

  • shipping: 貴社サイト上で指定した配送先住所
  • shipping_preference: "SET_PROVIDED_ADDRESS"
  • name: 購入者の指名
  • email_address: 購入者のメールアドレス
  • phone: 購入者の電話番号
  • address: 購入者の住所
  • invoice_id: 貴社で発番した一意の注文ID

サンプルリクエスト
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access token>" \
-d '{
  "intent": "CAPTURE",
  "application_context": {
    "shipping_preference": "SET_PROVIDED_ADDRESS",
    "brand_name": "Your Site Name"
  },
  "payer": {
    "name": {
      "given_name": "太郎",
      "surname": "青山"
    },
    "email_address": "buyer@example.com",
    "address": {
      "address_line_1": "青山1-2-3",
      "address_line_2": "ペイパルビル 1001",
      "admin_area_2": "港区",
      "admin_area_1": "東京都",
      "postal_code": "107-0001",
      "country_code": "JP"
    }
  },
  "purchase_units": [
    {
      "amount": {
        "currency_code": "JPY",
        "value": "1000",
        "breakdown": {
          "item_total": {
            "currency_code": "JPY",
            "value": "1000"
          }
        }
      },
      "items": [
        {
          "name": "Sample Goods",
          "unit_amount": {
            "currency_code": "JPY",
            "value": "1000"
          },
          "quantity": "1"
        }
      ],
      "shipping": {
        "name": {
          "full_name": "太郎 青山"
        },
        "address": {
          "address_line_1": "青山1-2-3",
          "address_line_2": "ペイパルビル 1001",
          "admin_area_2": "港区",
          "admin_area_1": "東京都",
          "postal_code": "107-0001",
          "country_code": "JP"
        }
      },
      "invoice_id": "貴社発番の一意の注文ID",
      "custom_id": "貴社指定のカスタムパラメータ値"
    }
  ]
}'

サンプルレスポンス
{
  "id": "3W9401969M757672V",
  "status": "CREATED",
  "links": [
    {
      "href": "https://api.sandbox.paypal.com/v2/checkout/orders/3W9401969M757672V",
      "rel": "self",
      "method": "GET"
    },
    {
      "href": "https://www.sandbox.paypal.com/checkoutnow?token=3W9401969M757672V",
      "rel": "approve",
      "method": "GET"
    },
    {
      "href": "https://api.sandbox.paypal.com/v2/checkout/orders/3W9401969M757672V",
      "rel": "update",
      "method": "PATCH"
    },
    {
      "href": "https://api.sandbox.paypal.com/v2/checkout/orders/3W9401969M757672V/capture",
      "rel": "capture",
      "method": "POST"
    }
  ]
}

決済を実行する

即時決済の場合は決済を実行するを行う
オーソリの場合は オーソリを実行するオーソリに対して決済を実行するを行う

サンプルHTMLフォームの /my-server/capture-order で下記 /v2/checkout/orders/{order_id}/capture を貴社サーバからコールし決済を実行します。

Capture an order
https://developer.paypal.com/docs/api/orders/v2/#orders_capture

以下のヘッダを追加してください。
"PayPal-Request-Id: Unique-Session-Identifier"

サンプルリクエスト
curl -v -X POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/{order_id}/capture' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <access-token>' \
-H 'PayPal-Request-Id: 7c3df866-90b7-4173-83e6-f2d2b53c8480'

サンプルレスポンス
{
  "id": "3W9401969M757672V",
  "status": "COMPLETED",
  "purchase_units": [
    {
      "reference_id": "default",
      "shipping": {
        "name": {
          "full_name": "太郎 青山"
        },
        "address": {
          "address_line_1": "青山1-2-3",
          "address_line_2": "ペイパルビル 1001",
          "admin_area_2": "港区",
          "admin_area_1": "東京都",
          "postal_code": "123-4567",
          "country_code": "JP"
        }
      },
      "payments": {
        "captures": [
          {
            "id": "0BW08839JT755205T",
            "status": "COMPLETED",
            "amount": {
              "currency_code": "JPY",
              "value": "1000"
            },
            "final_capture": true,
            "seller_protection": {
              "status": "ELIGIBLE",
              "dispute_categories": [
                "ITEM_NOT_RECEIVED",
                "UNAUTHORIZED_TRANSACTION"
              ]
            },
            "seller_receivable_breakdown": {
              "gross_amount": {
                "currency_code": "JPY",
                "value": "1000"
              },
              "paypal_fee": {
                "currency_code": "JPY",
                "value": "76"
              },
              "net_amount": {
                "currency_code": "JPY",
                "value": "924"
              }
            },
            "invoice_id": "your-own-uniqueid-for-an-order",
            "links": [
              {
                "href": "https://api.sandbox.paypal.com/v2/payments/captures/0BW08839JT755205T",
                "rel": "self",
                "method": "GET"
              },
              {
                "href": "https://api.sandbox.paypal.com/v2/payments/captures/0BW08839JT755205T/refund",
                "rel": "refund",
                "method": "POST"
              },
              {
                "href": "https://api.sandbox.paypal.com/v2/checkout/orders/3W9401969M757672V",
                "rel": "up",
                "method": "GET"
              }
            ],
            "create_time": "2021-01-21T07:21:27Z",
            "update_time": "2021-01-21T07:21:27Z"
          }
        ]
      }
    }
  ],
  "payer": {
    "name": {
      "given_name": "太郎",
      "surname": "青山"
    },
    "email_address": "buyer@example.com",
    "payer_id": "ZJEHNEECZ6DKS",
    "address": {
      "country_code": "JP"
    }
  },
  "links": [
    {
      "href": "https://api.sandbox.paypal.com/v2/checkout/orders/3W9401969M757672V",
      "rel": "self",
      "method": "GET"
    }
  ]
}

オーソリを実行する

即時決済の場合は決済を実行するを行う
オーソリの場合は オーソリを実行するオーソリに対して決済を実行するを行う

下記 /v2/checkout/orders/{id}/authorizeを貴社サーバからコールし決済を実行します。

Authorize an order
https://developer.paypal.com/docs/api/orders/v2/#orders_authorize

以下のヘッダを追加してください。
"PayPal-Request-Id: Unique-Session-Identifier"

サンプルリクエスト
curl -v -X POST 'https://api-m.sandbox.paypal.com/v2/checkout/orders/{order_id}/authorize' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <access-token>' \
-H 'PayPal-Request-Id: 7c3df866-90b7-4173-83e6-f2d2b53c8480'

サンプルレスポンス
{
  "id": "3W9401969M757672V",
  "status": "COMPLETED",
  "purchase_units": [
    {
      "reference_id": "default",
      "shipping": {
        "name": {
          "full_name": "太郎 青山"
        },
        "address": {
          "address_line_1": "青山1-2-3",
          "address_line_2": "ペイパルビル 1001",
          "admin_area_2": "港区",
          "admin_area_1": "東京都",
          "postal_code": "123-4567",
          "country_code": "JP"
        }
      },
      "payments": {
        "authorizations": [
          {
            "status": "CREATED",
            "id": "1T3898K57614G8S79",
            "amount": {
              "currency_code": "JPY",
              "value": "1000"
            },
            "invoice_id": "your-own-uniqueid-for-an-order",
            "seller_protection": {
              "status": "ELIGIBLE",
              "dispute_categories": [
                "ITEM_NOT_RECEIVED",
                "UNAUTHORIZED_TRANSACTION"
              ]
            },
            "expiration_time": "2021-02-20T05:38:07Z",
            "links": [
              {
                "href": "https://api.sandbox.paypal.com/v2/payments/authorizations/1T3898K57614G8S79",
                "rel": "self",
                "method": "GET"
              },
              {
                "href": "https://api.sandbox.paypal.com/v2/payments/authorizations/1T3898K57614G8S79/capture",
                "rel": "capture",
                "method": "POST"
              },
              {
                "href": "https://api.sandbox.paypal.com/v2/payments/authorizations/1T3898K57614G8S79/void",
                "rel": "void",
                "method": "POST"
              },
              {
                "href": "https://api.sandbox.paypal.com/v2/payments/authorizations/1T3898K57614G8S79/reauthorize",
                "rel": "reauthorize",
                "method": "POST"
              },
              {
                "href": "https://api.sandbox.paypal.com/v2/checkout/orders/32G78336EM1179410",
                "rel": "up",
                "method": "GET"
              }
            ],
            "create_time": "2021-01-22T05:38:07Z",
            "update_time": "2021-01-22T05:38:07Z"
          }
        ]
      }
    }
  ],
  "payer": {
    "name": {
      "given_name": "太郎",
      "surname": "青山"
    },
    "email_address": "buyer@example.com",
    "payer_id": "ZJEHNEECZ6DKS",
    "address": {
      "country_code": "JP"
    }
  },
  "links": [
    {
      "href": "https://api.sandbox.paypal.com/v2/checkout/orders/3W9401969M757672V",
      "rel": "self",
      "method": "GET"
    }
  ]
}

オーソリに対して決済を実行する

下記 /v2/payments/authorizations/{authorization_id}/captureを貴社サーバからコールし決済を実行します。

Capture an authorization for an order
https://developer.paypal.com/docs/api/payments/v2/#authorizations_capture

以下のヘッダを追加してください。
"PayPal-Request-Id: Unique-Session-Identifier"

また、以下のように金額を指定してオーソリ金額の一部金額を売上処理することが可能です。後で残りも売上処理する場合は final_captureにfalseを指定します。

{
  "amount": {
    "value": "700",
    "currency_code": "JPY"
  },
  "invoice_id" : "貴社発番の一意の注文ID",
  "final_capture": true
}

サンプルリクエスト
curl -v -X POST 'https://api-m.sandbox.paypal.com/v2/payments/authorizations/1T3898K57614G8S79/capture' \
-H 'Authorization: Bearer <Access-Token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-H 'PayPal-Request-Id: 1cc9e87b-0410-4999-9fef-76d732a94bc9'

サンプルレスポンス
{
  "id": "0VG7SW2984469872W",
  "status": "COMPLETED",
  "links": [
    {
      "href": "https://api.sandbox.paypal.com/v2/payments/captures/0VG7SW2984469872W",
      "rel": "self",
      "method": "GET"
    },
    {
      "href": "https://api.sandbox.paypal.com/v2/payments/captures/0VG7SW2984469872W/refund",
      "rel": "refund",
      "method": "POST"
    },
    {
      "href": "https://api.sandbox.paypal.com/v2/payments/authorizations/1T3898K57614G8S79",
      "rel": "up",
      "method": "GET"
    }
  ]
}


決済のキャンセル・返金

下記 /v2/payments/captures/{capture_id}/refund を貴社サーバからコールし決済のキャンセル、返金を実行します。

Refund captured payment

PayPalアカウントの残高から返金されますので、実行するには十分な残高が必要です。返金可能な期間は取引日から180日間です。

金額を指定し一部金額を返金することが可能です。

オーソリのキャンセル・返金

下記 /v2/payments/authorizations/{authorization_id}/void を貴社サーバからコールしオーソリのキャンセルを実行します。

Void authorized payment


事前承認支払いの実装について

事前承認支払いのご利用は審査が必要です。現在、詳しい実装方法はご利用可能なユーザー様に個別に案内しています。

その他

エラー処理

PayPal決済画面でカード与信エラーが起きた場合のリカバリー

https://developer.paypal.com/docs/checkout/integration-features/funding-failure/
(サーバーサイドの実装をしている場合のみ必要です)

セキュリティ対応

ベストプラクティス

住所連携方法(配送先連携による売り手保護、新規登録時の入力削減を実現します。JavaScript、サーバーサイド共にOrderのpurchanse_unit.shippingとして渡します):
https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit

PayPalアカウントの個人情報や配送先住所を取得する方法(ソーシャルログインのようにECサイトの配送先や会員情報の自動入力に利用できます):
https://developer.paypal.com/docs/checkout/integrate/#5-capture-the-transaction
(data、およびdetailsから取得できます)

商品情報の連携方法(支払い者の取引明細に表示されます。JavaScript、サーバーサイド共にOrderのpurchanse_unit.itemsとして渡します):
https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit

売り手保護対象かどうかの確認方法(Orderの詳細で確認できます):
https://developer.paypal.com/docs/api/orders/v2/#orders_get

PayPalボタンのカスタマイズ方法:
https://developer.paypal.com/docs/checkout/integration-features/customize-button/
https://developer.paypal.com/docs/checkout/reference/customize-sdk/
(読み込むJavaScriptのパラメータとJavaScriptの記述両方を使ってカスタマイズします)

事故決済の防止方法(請求IDによる二重決済防止、eCheckの無効化など):
https://developer.paypal.com/docs/classic/admin/setup-account/#block-accidental-payments
(eCheckの説明は以下の「アカウントの設定」の参照リンクにあります)

請求IDの実装方法:
https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit
(「invoice_id」で渡せます)

トラブルシューティング

JavaScriptのパフォーマンス対策:
https://developer.paypal.com/docs/checkout/troubleshoot/performance/

サポートブラウザ、IE対策:
https://developer.paypal.com/docs/checkout/troubleshoot/support/
https://developer.paypal.com/docs/checkout/troubleshoot/internet-explorer/

アカウントの設定

実装以外のアカウントの設定ポイントについては以下をご参照ください。
https://qiita.com/PPJP/items/9b089d44d48c9b245eb3

テストについて

テスト環境(Sandbox)

※エラーテストを行う際は、利用するSandboxのSellerアカウントのProfileの「Negative Testing」がONになっていることを確認してください。
https://qiita.com/PPJP/items/6ba65ed21da7fba65586#sandbox%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AEprofile%E3%81%AE%E8%A8%AD%E5%AE%9A

PayPalアカウントのカード与信エラーの発生方法:
カードの請求先住所(カードを登録する際に入力)の番地(Line1)に「CCREJECT-REFUSED」といれると、カード与信エラーが常に発生するようになります。このカードを決済時に利用することで、上記の「カード与信エラーが起きた場合のリカバリー」のテストができます。
https://developer.paypal.com/webapps/developer/docs/classic/express-checkout/ht_ec_fundingfailure10486/#test-a-sale-or-authorization-integration

Sandboxのテストに関するTIPS
https://developer.paypal.com/docs/classic/lifecycle/sb_tips

リリース方法

その他

リリース後の運用について

  • APIのレスポンスに含まれるdebug-id(correlation-id)、Authorize/CaptureのIDなどは必ずログに残すようにしてください。問い合わせ時に必要となります。
  • 本番やSandboxの状態確認や障害通知の受け取りはこちらから可能です。

ガイドとリファレンス

https://developer.paypal.com/docs/checkout/integration-features/
https://developer.paypal.com/docs/checkout/reference/

PayPalチェックアウトに使うAPIの詳細:
https://developer.paypal.com/docs/api/orders/v2/
https://developer.paypal.com/docs/api/payments/v2/

API Executor(APIの挙動を試すことができます):
https://www.paypal.com/apex/home

サンプルコードやソースコード

その他のソリューション

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
193
Help us understand the problem. What are the problem?