# Webhook なしでカード支払いを受け付ける

サーバでカード決済を確定して、カードの認証リクエストを処理する方法をご紹介します。

# React Native


より幅広いサポートと将来の保証のために、非同期的な支払いを目的とした[標準的な実装](https://docs.stripe.com/payments/accept-a-payment.md)を使用してください。

この組み込みは、*Webhook* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) を使用したり、オフラインイベントを処理したりせずに、クライアントからサーバーへの単一フローを使用して決済を受け付けます。この組み込みはシンプルに見えるかもしれませんが、ビジネスの成長に合わせて拡張するのが難しく、以下のような制限もあります。

- **カードのみをサポート**: ACH や現地で一般的な決済手段に個別に対応するには、追加のコードを記述する必要があります。
- **二重請求のリスク**: 顧客が支払おうとするたびに新たな PaymentIntent を同期的に作成することにより、顧客に対して誤って二重請求が行われるリスクがあります。必ず[ベストプラクティス](https://docs.stripe.com/error-low-level.md#should-retry)に従ってください。
- **手動認証処理**: 3D セキュアを備えたカード、または*強力な顧客認証 (SCA)* (Strong Customer Authentication (SCA) is a regulatory requirement in effect as of September 14, 2019, that impacts many European online payments. It requires customers to use two-factor authentication like 3D Secure to verify their purchase) などの規制の対象となるカードでは、クライアント側での追加の手順が必要になります。

この実装の使用を選択した場合は、上記の制限にご注意ください。制限を設けたくない場合は、[標準的な実装](https://docs.stripe.com/payments/accept-a-payment.md)を使用します。

## Stripe を設定する [サーバ側] [クライアント側]

### サーバ側

この組み込みには、Stripe API と通信するエンドポイントがサーバ上に必要です。Stripe の公式ライブラリを使用して、サーバから Stripe API にアクセスします。

#### Ruby

```bash
# Available as a gem
sudo gem install stripe
```

```ruby
# If you use bundler, you can add this line to your Gemfile
gem 'stripe'
```

### クライアント側

[React Native SDK](https://github.com/stripe/stripe-react-native) はオープンソースであり、詳細なドキュメントが提供されています。内部では、[ネイティブの iOS](https://github.com/stripe/stripe-ios) および [Android](https://github.com/stripe/stripe-android) の SDK を使用します。Stripe の React Native SDK をインストールするには、プロジェクトのディレクトリーで (使用するパッケージマネージャーによって異なる) 次のいずれかのコマンドを実行します。

#### yarn

```bash
yarn add @stripe/stripe-react-native
```

#### npm

```bash
npm install @stripe/stripe-react-native
```

次に、その他の必要な依存関係をインストールします。

- iOS の場合は、**ios** ディレクトリに移動して `pod install` を実行し、必要なネイティブ依存関係もインストールします。
- Android の場合は、依存関係をインストールする必要はありません。

> [公式の TypeScript ガイド](https://reactnative.dev/docs/typescript#adding-typescript-to-an-existing-project)に従って TypeScript のサポートを追加することをお勧めします。

### Stripe の初期化

React Native アプリで Stripe を初期化するには、決済画面を `StripeProvider` コンポーネントでラップするか、`initStripe` 初期化メソッドを使用します。`publishableKey` の API [公開可能キー](https://docs.stripe.com/keys.md#obtain-api-keys)のみが必要です。次の例は、`StripeProvider` コンポーネントを使用して Stripe を初期化する方法を示しています。

```jsx
import { useState, useEffect } from 'react';
import { StripeProvider } from '@stripe/stripe-react-native';

function App() {
  const [publishableKey, setPublishableKey] = useState('');

  const fetchPublishableKey = async () => {
    const key = await fetchKey(); // fetch key from your server here
    setPublishableKey(key);
  };

  useEffect(() => {
    fetchPublishableKey();
  }, []);

  return (
    <StripeProvider
      publishableKey={publishableKey}
      merchantIdentifier="merchant.identifier" // required for Apple Pay
      urlScheme="your-url-scheme" // required for 3D Secure and bank redirects
    >
      {/* Your app code here */}
    </StripeProvider>
  );
}
```

> テストおよび開発時には API の[テストキー](https://docs.stripe.com/keys.md#obtain-api-keys)を使用し、アプリの公開時には[本番環境](https://docs.stripe.com/keys.md#test-live-modes)キーを使用します。

## 決済ページを作成する [クライアント側]

カード番号、有効期限、セキュリティコード、郵便番号を収集する、SDK が提供する UI コンポーネント、`CardField` を使用して、クライアント側でカード情報を安全に収集します。
![](https://d37ugbyn3rpeym.cloudfront.net/docs/mobile/ios/card-field.mp4)
`CardField` コンポーネントを支払い画面に追加することで、顧客からカード詳細を安全に収集します。`onCardChange` コールバックを使用して、カードのブランドや詳細情報の欠落の有無など、カードに関する機密性の低い情報を検査します。

```javascript
import { CardField, useStripe } from '@stripe/stripe-react-native';

function PaymentScreen() {
  // ...
  return (
    <View>
      <CardField
        postalCodeEnabled={true}
        placeholders={{
          number: '4242 4242 4242 4242',
        }}
        cardStyle={{
          backgroundColor: '#FFFFFF',
          textColor: '#000000',
        }}
        style={{
          width: '100%',
          height: 50,
          marginVertical: 30,
        }}
        onCardChange={(cardDetails) => {
          console.log('cardDetails', cardDetails);
        }}
        onFocus={(focusedField) => {
          console.log('focusField', focusedField);
        }}
      />
    </View>
  );
}
```

アプリを実行し、決済ページに `CardField` コンポーネントが表示されることを確認します。

## カード詳細を収集する [クライアント側]

顧客が購入する準備が整ったら、`CardField` コンポーネントによって収集された詳細情報を使用して *PaymentMethod (支払い方法)* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) を作成します。

```typescript
import { CardField, useStripe } from '@stripe/stripe-react-native';
function PaymentScreen() {
  const { createPaymentMethod, handleNextAction } = useStripe();
  const pay = async () => {
    // Gather customer billing information (for example, email)
    const billingDetails: CreatePaymentMethod.BillingDetails = {
      email: 'email@stripe.com',
      phone: '+48888000888',
      addressCity: 'Houston',
      addressCountry: 'US',
      addressLine1: '1459  Circle Drive',
      addressLine2: 'Texas',
      addressPostalCode: '77063',
    };
    // Create payment method
    const { paymentMethod, error } = await createPaymentMethod({
      paymentMethodType: 'Card',
      paymentMethodData: {
        billingDetails,
      }
    });
  };
  // ...
}
```

## サーバに PaymentMethod を送信する [クライアント側]

PaymentMethod が正常に作成されたら、その ID をサーバに送信します。

```javascript
// ...
const pay = async () => {
  // ...
  // Send the PaymentMethod to your server to create a PaymentIntent
  const response = await fetch(`/pay`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ paymentMethodId: paymentMethod.id }),
  });
  const { error, requires_action, payment_intent_client_secret } = await response.json();
  if (error) {
    // Error creating or confirming PaymentIntent
    Alert.alert('Error', paymentIntentError);
    return;
  }
  if (payment_intent_client_secret && !requires_action) {
    // Payment succeeded
    Alert.alert('Success', 'The payment was confirmed successfully!');
  }
  if (payment_intent_client_secret && requires_action) {
    // ...continued below
  }
};
// ...
```

## PaymentIntent を作成する [サーバ側]

リクエストを受信するためにサーバにエンドポイントを設定します。このエンドポイントは、[後で](https://docs.stripe.com/payments/accept-a-payment-synchronously.md#confirm-payment) でも、追加の認証ステップが必要なカードの処理に使用されます。

クライアント側で作成した [PaymentMethod)](https://docs.stripe.com/api/payment_methods/object.md) の ID を使って[新しい PaymentIntent を作成](https://docs.stripe.com/payments/payment-intents.md#creating-a-paymentintent)します。PaymentIntent の作成時に [confirm](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-confirm) プロパティを true に設定するか、作成後に [confirm](https://docs.stripe.com/api/payment_intents/confirm.md) を呼び出すことにより、PaymentIntent を*確定* (Confirming a PaymentIntent indicates that the customer intends to pay with the current or provided payment method. Upon confirmation, the PaymentIntent attempts to initiate a payment)できます。カード支払いでは、[オーソリとキャプチャーの分離](https://docs.stripe.com/payments/place-a-hold-on-a-payment-method.md)もサポートされています。

支払いで3D セキュア認証などの追加アクションが必要な場合、PaymentIntent のステータスは `requires_action` に設定されます。支払いが失敗すると、ステータスは `requires_payment_method` に戻され、ユーザにエラーを表示する必要があります。支払いで追加認証が求められない場合は、支払いが作成され、PaymentIntent のステータスは `succeeded` に設定されます。

> [2019-02-11](https://docs.stripe.com/upgrades.md#2019-02-11) 以前の API のバージョンでは、`requires_payment_method` の代わりに `requires_source`、`requires_action` の代わりに `requires_source_action` が表示されます。

#### curl

```bash
curl https://api.stripe.com/v1/payment_intents \
  -u <<YOUR_SECRET_KEY>>: \
  -d "payment_method"="{{PAYMENT_METHOD_ID}}" \
  -d "amount"=1099 \
  -d "currency"="usd" \
  -d "confirmation_method"="manual" \
  -d "confirm"="true"
```

後で再利用するためにカードを保存する場合は、*PaymentMethod* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) を格納するため [Customer](https://docs.stripe.com/api/customers/create.md) を作成して、PaymentIntent の作成時に以下の追加パラメータを渡します。

- [顧客](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer)。*Customer* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments) の ID に設定します。
- [setup_future_usage](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-setup_future_usage):`off_session` に設定し、顧客が存在しないときの*オフセッションの支払い* (A payment is described as off-session if it occurs without the direct involvement of the customer, using previously-collected payment information)にこの PaymentMethod を再利用する予定であることを Stripe に伝えます。このプロパティーを設定すると、PaymentIntent が確定され、ユーザーによる必要な操作がすべて完了した後で、PaymentMethod が顧客に保存されます。詳細については、[支払い後のカード保存](https://github.com/stripe-samples/saving-card-after-payment/tree/master/without-webhooks)のコード例をご覧ください。

## 次のアクションを処理する [クライアント側]

通常、ステップ 4 のサーバーでの*確定後* (Confirming an intent indicates that the customer intends to use the current or provided payment method. Upon confirmation, the intent attempts to initiate the portions of the flow that have real-world side effects)に支払いは成功しますが、決済フローによっては、*3D セキュア* (3D Secure (3DS) provides an additional layer of authentication for credit card transactions that protects businesses from liability for fraudulent card payments)による認証など、顧客による追加の対応が必要になることがあります。

​​次のアクションが必要なケースでは、PaymentIntentのステータスは `requires_action` になります。クライアント側でサーバーに PaymentIntent をリクエストし、その *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)) を `handleNextAction` に保存します。ネイティブハンドラはビューを表示し、顧客に認証フローを案内します。クライアントで必要なアクションを処理すると、PaymentIntent のステータスは `requires_confirmation` となります。このステップにより、連携はバックエンドで注文のフルフィルメントを行い、*フルフィルメント* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected)の結果をクライアントに返すことができます。

PaymentIntent ID をバックエンドに送信し、1 時間以内に再度確定して、支払いを完了します。1 時間以内に確定しない場合、支払いの試行は失敗し、`requires_payment_method` に戻ります。

```javascript
// ...
const pay = async () => {
  // ...
  // If PaymentIntent requires action, call handleNextAction
  if (payment_intent_client_secret && requires_action) {
    const { error, paymentIntent } = await handleNextAction(payment_intent_client_secret);
    if (error) {
      Alert.alert(`Error code: ${error.code}`, error.message);
    } else if (paymentIntent) {
        if (
          paymentIntent.status === PaymentIntents.Status.RequiresConfirmation
        ) {
          // Confirm the PaymentIntent again on your server
          const response = await fetch(`/pay`, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({ payment_intent_id: paymentIntent.id }),
          });
          const { error, success } = await response.json();
          if (error) {
            // Error during confirming Intent
            Alert.alert('Error', error);
          } else if (success) {
            Alert.alert('Success', 'The payment was confirmed successfully!');
          }
        } else {
          // Payment succedeed
          Alert.alert('Success', 'The payment was confirmed successfully!');
        }
      }
  }
};
// ...
```

## PaymentIntent を再度確定する [サーバ側]

このコードは、直前のステップでの処理と同様に、支払いに追加の認証が必要な場合にのみ実行されます。どの支払いでもこの追加ステップが必要になる場合があるため、コード自体はオプションではありません。

[上記](https://docs.stripe.com/payments/accept-a-payment-synchronously.md#create-payment-intent)で設定したものと同じエンドポイントを使用し、PaymentIntent を再度*確定* (Confirming an intent indicates that the customer intends to use the current or provided payment method. Upon confirmation, the intent attempts to initiate the portions of the flow that have real-world side effects)することにより、支払いを完了して注文の*フルフィルメントを実行* (Fulfillment is the process of providing the goods or services purchased by a customer, typically after payment is collected)します。この確定は支払い試行から 1 時間以内に実行してください。実行されない場合は、支払いが失敗して取引が `requires_payment_method` に戻されます。

#### curl

```bash
curl https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/confirm \
  -u <<YOUR_SECRET_KEY>>: \
  -X "POST"
```

## 組み込みをテストする

​​この実装の準備ができていることを確認するために、サンドボックスで使用できるテストカードがいくつかあります。任意のセキュリティコードおよび将来の有効期限を指定して使用します。

| 番号               | 説明                                      |
| ---------------- | --------------------------------------- |
| 4242424242424242 | 支払いが成功し、すぐに処理されます。                      |
| 4000002500003155 | 認証が必要です。Stripe は、顧客に認証を求めるモーダルをトリガーします。 |
| 4000000000009995 | 常に支払い拒否コード `insufficient_funds` で失敗します。 |

テストカードの一覧については、[テスト](https://docs.stripe.com/testing.md)に関するガイドを参照してください。

## Optional: セキュリティコードの再収集

保存されたカードで以降の決済を作成する際には、不正利用の追加対策としてカードのセキュリティコード (CVC) を再取得して、ユーザーを確認することをお勧めします。

まず、*Customer* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments) に関連付けられている決済手段を[リスト](https://docs.stripe.com/api/payment_methods/list.md)して、セキュリティコードの再収集のためにどの決済手段を表示するかを決定します。顧客のセキュリティコード情報を再収集したら、`createTokenForCVCUpdate` でセキュリティコードをトークン化します。

```javascript
function PaymentScreen() {
  // ...
  const { createTokenForCVCUpdate } = useStripe();
  const tokenizeCVC = async () => {
    const { error, tokenId } = await createTokenForCVCUpdate();
    if (error) {
      // handle error
    } else if (tokenId) {
      // pass the token ID to your backend
    }
  };
}
```

セキュリティコードトークンをサーバに送信したら、`payment_method_options[card][cvc_token]` パラメータの金額、通貨、およびセキュリティコードトークンを使用してサーバ上に PaymentIntent を作成します。

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d payment_method={{PAYMENT_METHOD_ID}} \
  -d customer={{CUSTOMER_ID}} \
  -d amount=1099 \
  -d currency=usd \
  -d confirmation_method=manual \
  -d confirm=true \
  -d "payment_method_options[card][cvc_token]={{CVC_TOKEN_ID}}"
```

A payment might succeed even with a failed CVC check. To prevent this, configure your [Radar rules](https://docs.stripe.com/radar/rules.md#traditional-bank-checks) to block payments when CVC verification fails.

