PayPal | アドバンスドチェックアウトで決済ボタン(住所なし)を実装する方法

はじめに

本記事では、PayPalの「アドバンスドチェックアウト」を使用して、郵送が不要な商品の決済ボタン(例:会費やデジタル商品)を実装する手順について解説します。PayPalのサンドボックス環境と本番環境をスムーズに切り替えながら、決済プロセスを簡単に統合する方法を紹介します。

1. PayPalアドバンスドチェックアウトとは?

PayPalアドバンスドチェックアウトは、WebサイトやアプリにPayPal決済機能を簡単に統合できる便利なソリューションです。この機能を使用することで、ユーザーはPayPalアカウントやクレジットカードを使用して、スムーズに支払いを完了できます。

本記事では、特に郵送が不要な商品の決済(例:会費、デジタル商品)に焦点を当て、PayPalの「NO_SHIPPING」オプションを使用して、住所の入力をスキップする方法を紹介します。

2. 必要な設定と準備

PayPalアカウントの準備

まず、PayPalのビジネスアカウントを作成し、PayPal Developer Dashboardからサンドボックスおよび本番環境用のクライアントIDとシークレットを取得します。

環境設定

次に、テストと本番の両方の環境を切り替えられるように設定を行います。この設定は、config.phpファイルで行います。

3. ファイル構成の紹介

実装する際のファイル構成は以下のようになります。

/paypal_checkout_btn/
│
├── index.php          // メインのフロントエンドファイル。決済ボタンを表示する
├── config.php         // PayPalの設定や定数を管理するファイル
├── create-payment.php // 支払いの作成を処理するバックエンドファイル
└── capture-payment.php// 支払いのキャプチャを処理するバックエンドファイル

4. 決済ボタンの実装手順

クライアントIDとシークレットの設定

config.phpにて、サンドボックスと本番環境用のクライアントIDとシークレットを定義し、環境を簡単に切り替えられるように設定します。

<?php
// /paypal_checkout_btn/config.php

// 環境の設定: 'sandbox' または 'live'
define('PAYPAL_ENVIRONMENT', 'sandbox'); // 'sandbox' から 'live' に変更して環境を切り替え

// PayPal クライアントIDとシークレット(sandbox と live の両方を定義)
if (PAYPAL_ENVIRONMENT === 'sandbox') {
    define('CLIENT_ID', 'YOUR_SANDBOX_CLIENT_ID');
    define('CLIENT_SECRET', 'YOUR_SANDBOX_CLIENT_SECRET');
} else {
    define('CLIENT_ID', 'YOUR_LIVE_CLIENT_ID');
    define('CLIENT_SECRET', 'YOUR_LIVE_CLIENT_SECRET');
}

// 決済金額を定数で管理
define('PAYMENT_AMOUNT', 5000); // 例: 5000円

// 環境に応じたエンドポイントの設定
define('PAYPAL_API_BASE_URL', PAYPAL_ENVIRONMENT === 'sandbox' 
    ? 'https://api-m.sandbox.paypal.com' 
    : 'https://api-m.paypal.com');
?>

決済金額の定数管理

決済金額もconfig.phpで定数として管理し、サイト全体で一貫性のある処理を実現します。

サンドボックスと本番環境の切り替え

config.phpファイルで、環境に応じてPayPalのAPIエンドポイントを動的に切り替える設定を行います。これにより、開発中のテストと本番環境での実運用がスムーズに行えます。

5. 配送が必要な商品と不要な商品の設定の違い

PayPalアドバンスドチェックアウトを利用する際、配送が必要な商品と不要な商品の設定に違いがあります。以下にその違いを説明します。

配送が不要な商品の設定

配送が不要な商品(例:デジタル商品、会費など)の場合、ユーザーから配送先住所を取得する必要がありません。このため、PayPal APIのリクエストでshipping_preference"NO_SHIPPING"に設定します。これにより、住所入力をスキップし、ユーザーの手間を省くことができます。

// 配送が不要な商品の場合
"application_context" => array(
    "shipping_preference" => "NO_SHIPPING" // 配送先住所の入力をスキップ
)

配送が必要な商品の設定

一方、配送が必要な商品(例:物理商品)の場合は、ユーザーから正確な配送先住所を取得する必要があります。このため、shipping_preferenceをデフォルトのままにし、PayPalがユーザーに住所入力を求めるようにします。

// 配送が必要な商品の場合
"application_context" => array(
    "shipping_preference" => "GET_FROM_FILE" // PayPalに登録された住所を使用
)

入力項目の違い

  • 配送が不要な商品: 郵便番号、電話番号、メールアドレスは入力されますが、住所の入力が省略されます。
  • 配送が必要な商品: 郵便番号、電話番号、メールアドレスに加え、配送先住所(国、都道府県、市区町村、番地など)の入力が求められます。

6. コードの全体像

以下に、PayPal決済ボタンの実装コードの全体像を紹介します。

index.php

<?php
require __DIR__ . '/config.php';
?>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PayPal決済 - 会費支払い</title>
    <!-- PayPal SDKの読み込み。クライアントIDは config.php で管理 -->
    <script src="https://www.paypal.com/sdk/js?client-id=<?php echo CLIENT_ID; ?>&currency=JPY"></script>
</head>
<body>
    <h1>会費支払いページ</h1>
    <div id="paypal-button-container"></div>

    <script>
        // config.php で設定された決済金額を取得
        const paymentAmount = <?php echo PAYMENT_AMOUNT; ?>;

        paypal.Buttons({
            createOrder: function(data, actions) {
                // create-payment.php に決済金額を送信して支払いを作成
                return fetch('/paypal_checkout_btn/create-payment.php', {
                    method: 'post',
                    headers: {
                        'content-type': 'application/json'
                    },
                    body: JSON.stringify({
                        amount: paymentAmount
                    })
                }).then(function(res) {
                    return res.json();
                }).then(function(orderData) {
                    return orderData.id; // 支払いIDを取得
                });
            },
            onApprove: function(data, actions) {
                // 支払いをキャプチャするため、capture-payment.php に orderID を送信
                return fetch('/paypal_checkout_btn/capture-payment.php', {
                    method: 'post',
                    headers: {
                        'content-type': 'application/json'
                    },
                    body: JSON.stringify({
                        orderID: data.orderID
                    })
                }).then(function(res) {
                    return res.json();
                }).then(function(orderData) {
                    alert('支払いが完了しました!');
                });
            },
            onError: function(err) {
                console.error('エラーが発生しました:', err);
                alert('エラーが発生しました。');
            }
        }).render('#

paypal-button-container');
    </script>
</body>
</html>

create-payment.php

<?php
header('Content-Type: application/json');
require __DIR__ . '/config.php';

// PayPalのAPIを使用して支払いを作成する
$clientId = CLIENT_ID;
$clientSecret = CLIENT_SECRET;

function getAccessToken($clientId, $clientSecret) {
    $url = PAYPAL_API_BASE_URL . '/v1/oauth2/token';
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_USERPWD, $clientId . ":" . $clientSecret);
    curl_setopt($curl, CURLOPT_POSTFIELDS, "grant_type=client_credentials");
    $response = curl_exec($curl);
    if ($response === false) {
        error_log('cURL Error: ' . curl_error($curl));
        die('Error: ' . curl_error($curl));
    }
    curl_close($curl);
    $jsonResponse = json_decode($response, true);
    return $jsonResponse['access_token'];
}

function createPayment($accessToken, $amount) {
    $url = PAYPAL_API_BASE_URL . '/v2/checkout/orders';
    $paymentData = array(
        "intent" => "CAPTURE",
        "purchase_units" => array(
            array(
                "amount" => array(
                    "currency_code" => "JPY",
                    "value" => $amount
                )
            )
        ),
        "application_context" => array(
            "shipping_preference" => "NO_SHIPPING" // 配送不要の場合の設定
        )
    );
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_HTTPHEADER, array(
        "Content-Type: application/json",
        "Authorization: Bearer " . $accessToken
    ));
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($paymentData));
    $response = curl_exec($curl);
    if ($response === false) {
        error_log('cURL Error: ' . curl_error($curl));
        die('Error: ' . curl_error($curl));
    }
    curl_close($curl);
    return json_decode($response, true);
}

// リクエストされた金額を取得し、PayPal APIで支払いを作成
$input = json_decode(file_get_contents('php://input'), true);
$amount = $input['amount'];
$accessToken = getAccessToken($clientId, $clientSecret);
$payment = createPayment($accessToken, $amount);
echo json_encode($payment);
?>

capture-payment.php

<?php
header('Content-Type: application/json');
require __DIR__ . '/config.php';

// 支払いをキャプチャする
$clientId = CLIENT_ID;
$clientSecret = CLIENT_SECRET;

function getAccessToken($clientId, $clientSecret) {
    $url = PAYPAL_API_BASE_URL . '/v1/oauth2/token';
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_USERPWD, $clientId . ":" . $clientSecret);
    curl_setopt($curl, CURLOPT_POSTFIELDS, "grant_type=client_credentials");
    $response = curl_exec($curl);
    if ($response === false) {
        error_log('cURL Error: ' . curl_error($curl));
        die('Error: ' . curl_error($curl));
    }
    curl_close($curl);
    $jsonResponse = json_decode($response, true);
    return $jsonResponse['access_token'];
}

function capturePayment($accessToken, $orderID) {
    $url = PAYPAL_API_BASE_URL . '/v2/checkout/orders/' . $orderID . '/capture';
    $curl = curl_init($url);
    curl_setopt($curl, CURLOPT_HTTPHEADER, array(
        "Content-Type: application/json",
        "Authorization: Bearer " . $accessToken
    ));
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($curl);
    if ($response === false) {
        error_log('cURL Error: ' . curl_error($curl));
        die('Error: ' . curl_error($curl));
    }
    curl_close($curl);
    return json_decode($response, true);
}

try {
    // リクエストされた orderID を取得し、PayPal APIで支払いをキャプチャ
    $input = json_decode(file_get_contents('php://input'), true);
    if (!$input || !isset($input['orderID'])) {
        throw new Exception('Invalid input');
    }
    $orderID = $input['orderID'];
    $accessToken = getAccessToken($clientId, $clientSecret);
    $capture = capturePayment($accessToken, $orderID);
    echo json_encode($capture);
} catch (Exception $e) {
    error_log('Error: ' . $e->getMessage());
    http_response_code(500);
    echo json_encode(['error' => $e->getMessage()]);
}
?>

7. テストと確認

サンドボックス環境で実際にテストを行い、決済フローが期待通りに動作することを確認します。特に、エラーが発生した場合には、ログに記録された情報をもとに修正を行ってください。

8. まとめ

この記事では、PayPalアドバンスドチェックアウトを使用した会費などの郵送が不要な商品の決済ボタンを実装する手順を解説しました。環境設定やサンドボックスでのテストを適切に行うことで、開発から本番運用までスムーズに移行できるようになります。

コメント

タイトルとURLをコピーしました