PAY.JP
PAY.JP

PayPay 決済の実装

PayPay 決済を実装し、顧客からコード決済を受け付ける

PayPay 決済を実装する方法は2つあります。

Checkout v2 (外部型リンク決済)を使用する場合

PAY.JP がホスティングする Checkout v2 決済ページにユーザーをリダイレクトするだけで、PayPay 決済を含む複数の支払い方法に対応できます。

Checkout Session の作成例
curl https://api.pay.jp/v2/checkout/sessions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d '{
    "payment_method_types": ["card"],   
    "payment_method_types": ["card", "paypay"],  
    "line_items": [
      {
        "quantity": 1,
        "price": "price_xxxxx"
      }
    ],
    "mode": "payment",
    "success_url": "https://example.com/success?session_id={CHECKOUT_SESSION_ID}",
    "cancel_url": "https://example.com/cancel"
  }'

レスポンスに含まれる url にユーザーをリダイレクトしてください。

詳細な実装方法については、外部リンク型決済を使うをご覧ください。

Payments Widgets (埋め込み型決済)を使用する場合

外部ドメインではなく、加盟店のサイト上で決済フローを管理したい場合は Payments Widgets を使用します。クライアント側とサーバー側でそれぞれの実装が必要です。

1. サーバーサイドで Payment Flow を作成

サーバー側に Payment Flow を作成するエンドポイントを用意します。
このとき、Payment Flow の client_secret が必要になるので、レスポンスとして返却するようにします。

server.ts
app.post('/api/create-payment-flow', async (req, res) => {
  try {
    const { amount } = req.body

    const { data: paymentFlow } = await createPaymentFlow({
      client,
      body: {
        amount: amount,
        payment_method_types: ['paypay']
      }
    })

    res.json({
      client_secret: paymentFlow.client_secret
    })
  } catch (error) {
    console.error('Payment Flow creation failed:', error)
    res.status(500).json({
      error: 'Payment Flow の作成に失敗しました。'
    })
  }
})

2. クライアント側で Payment Widgets を使用して決済フォームを表示

1 で作成したエンドポイントを呼び出し、Payment Flow を作成します。
Payment Flow の client_secret を取得したら、payments-js ライブラリを使用して Payment Widgets を初期化し、決済フォームを表示します。

Payments Widgets の使用例
import { loadPayments } from '@payjp/payments-js';

// Payment Flow を作成して client_secret を取得
const response = await fetch('/api/create-payment-flow', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ amount: 1000 })
});
const { client_secret } = await response.json();

const payments = await loadPayments('pk_test_xxxxx');
const widgets = payments.widgets({ clientSecret: client_secret });

// Payment Widgets を作成してマウント
const paymentForm = widgets.createForm('payment', {
  paymentMethodOrder: ['card', 'paypay']
});

paymentForm.mount('#payment-form');

3. 決済の確定

ユーザーは表示された決済フォームで必要事項を入力します。
加盟店側で送信ボタンを用意し、クリックイベントなどを拾って決済情報を送信します。
PayPay 決済では、ユーザーは PayPay アプリまたは Web ページにリダイレクトされ、決済を完了します。

client.tsx
const submitButton = document.getElementById('submit-button');

submitButton.addEventListener('click', async () => {
  const { error } = await widgets.confirmPayment({
    return_url: 'https://example.com/payment/complete'
  });

  if (error) {
    console.error('決済エラー:', error);
    // エラーメッセージを表示
    return;
  }

  // PayPay アプリまたは Web ページにリダイレクトされます
});

4. 決済結果の確認

決済完了後、指定した return_url にユーザーがリダイレクトされます。このとき、URL には以下のパラメータが付与されています。

パラメータ説明
payment_flow_idPayment Flow の ID
payment_flow_client_secretPayment Flow の client_secret

決済結果を確認する方法は2つあります。

方法1: フロントエンドで決済結果を確認する

payments-js の retrievePaymentFlow メソッドを使用して、フロントエンドのみで決済結果を確認できます。

complete.tsx
import { loadPayments } from '@payjp/payments-js';

// URL パラメータから client_secret を取得
const query = new URLSearchParams(window.location.search);
const clientSecret = query.get('payment_flow_client_secret');

// payments-js を使って Payment Flow を取得
const payments = await loadPayments('pk_test_xxxxx');
const paymentFlow = await payments.retrievePaymentFlow(clientSecret);

if (paymentFlow.status === 'succeeded') {
  // 決済成功画面を表示
  console.log('決済が完了しました');
} else {
  // 決済失敗画面を表示
  console.log('決済に失敗しました');
}

方法2: サーバーサイドで決済結果を確認する

Payment Flow ID を使用してサーバーサイドで決済結果を確認する方法です。 サーバーに確認用のエンドポイントを作成し、フロントエンドからそのエンドポイントを呼び出して結果を取得します。

1. 決済ステータス確認エンドポイントの作成

まず、サーバーに Payment Flow の決済ステータスを確認するエンドポイントを作成します。

server.ts
app.post('/api/check-payment-status', async (req, res) => {
  try {
    const { payment_flow_id } = req.body

    // Payment Flow のステータスを取得
    const { data: paymentFlow } = await getPaymentFlow({
      client,
      path: { payment_flow_id }
    })

    res.json({
      status: paymentFlow.status
    })
  } catch (error) {
    console.error('Status check failed:', error)
    res.status(500).json({
      error: 'ステータスの確認に失敗しました。'
    })
  }
})

2. 決済ステータス確認エンドポイントの呼び出し

フロントエンドから上記のエンドポイントを呼び出し、決済結果を取得します。

complete.tsx
// URL パラメータから情報を取得
const query = new URLSearchParams(window.location.search);
const paymentFlowId = query.get('payment_flow_id');

// サーバーにステータス確認をリクエスト
const response = await fetch('/api/check-payment-status', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    payment_flow_id: paymentFlowId
  })
});

const { status } = await response.json();

if (status === 'succeeded') {
  // 決済成功画面を表示
  console.log('決済が完了しました');
} else {
  // 決済失敗画面を表示
  console.log('決済に失敗しました');
}

関連リソース