PAY.JP
3Dセキュア導入方法
これは PAY.JP API v1 のドキュメントです。v2 のドキュメントはこちら、v1 からの移行は移行ガイドをご覧ください。

モバイル SDK を利用して 3Dセキュアを導入する

モバイル SDK でトークン作成時に 3Dセキュアを導入する

ここでは、モバイル SDK を利用してトークン作成時に 3Dセキュアを導入する方法について説明します。
3Dセキュアの概要については、下記ドキュメントをご覧ください。

リダイレクト URL を設定する(各 SDK 共通)

まずはじめに、管理画面 > API設定 > 3Dセキュアからリダイレクト URL を設定します。

モバイル SDK では、カード会社が提供する 3Dセキュア認証画面を表示するためにブラウザアプリを利用します。 認証を実施した後、モバイルアプリで再度処理を継続するためにリダイレクト URL とその識別子を登録します(ここで設定した URL と識別子は実装に利用します)。

URL スキームは HTTPS 以外にカスタム URL スキームを利用できます。 HTTPS スキームの URL の場合、iOS では Universal Link、Android では App Links の設定を行ってください。

3Dセキュア開始方法

各 SDK でカードフォーム開始時にオプションを指定することで、トークン作成時に 3Dセキュアを開始できます。

また、管理画面 > API設定 > 3Dセキュアよりトークン 3Dセキュアの実施モードを指定することで、パラメーターを指定することなく全てのトークン作成に 3Dセキュアを必須にすることが可能です。

管理画面でトークン 3Dセキュアを実施するモードが有効になっている場合、たとえ SDK でカードフォーム開始時に 3Dセキュアを実施しないオプションを指定しても、3Dセキュアは実施されます。

管理画面でのモードSDK でのオプション( useThreeDSecure )トークン作成時の3Dセキュア
有効true実施
有効false実施
無効true実施
無効false実施されない

ここから先の設定方法は、各 SDK によって異なります。利用する SDK に合わせて設定を追加してください。

payjp-iosの場合

XcodeでURLスキームを設定する

管理画面で追加したリダイレクト URL の設定に合わせて、アプリにも URL スキームの設定をします。

参考: Defining a Custom URL Scheme for Your App | Apple Developer Documentation

Xcode を開き、プロジェクト設定でアプリのターゲットを選択し Info タブに切り替えます。 URL Types という項目の + ボタンからスキーム設定を追加し、管理画面で登録したリダイレクト URL のスキームを設定します。

たとえば、リダイレクト URL が jp.pay.example://tds/finish の場合、以下のように jp.pay.example を設定します。

リダイレクトURLを登録する

管理画面で指定したリダイレクト URL と識別子を登録します。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {

    PAYJPSDK.publicKey = YourPayjpPublicKey
    PAYJPSDK.locale = Locale.current
    PAYJPSDK.threeDSecureURLConfiguration =
        ThreeDSecureURLConfiguration(redirectURL: URL(string: "jp.pay.example://tds/finish")!,
                                     redirectURLKey: "mobileapp")

    return true
}

カスタム URL スキームによってアプリが起動された際、 ThreeDSecureProcessHandler の completeThreeDSecureProcess(url:) を呼び出すことで、認証フローを完了します。

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    return ThreeDSecureProcessHandler.shared.completeThreeDSecureProcess(url: url)
}

カード所有者を 3Dセキュアに誘導する

カードフォーム画面を利用している場合

CardFormViewController.createCardFormViewController() を利用してカードフォーム画面を呼び出している場合は、SDK が自動的にハンドリングするため、カードフォーム開始時に 3Dセキュアの実施を指定するのみです。

let cardFormViewController = CardFormViewController.createCardFormViewController(
    delegate: self, 
    useThreeDSecure: true
)

それ以外の場合

3Dセキュアの認証を実施するには、 createToken の引数 useThreeDSecure に true を指定します。

createToken によって取得した Token の Token.card.threeDSecureStatus が .unverified の場合、3Dセキュアの認証を開始してください。

まず、 ThreeDSecureProcessHandler.startThreeDSecureProcess(viewController:delegate:token:) の第3引数に取得した Token を渡します。

これにより、SDK はブラウザを開きカード所有者を認証画面へと誘導します。認証が完了、失敗、またはキャンセルによって終了するまで、SDK によるトークン作成処理は中断状態となります。

self.cardFormView.createToken(useThreeDSecure: true) { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .success(let token):
        if tdsStatus = token.card.threeDSecureStatus, status == .unverified {
            ThreeDSecureProcessHandler.shared.startThreeDSecureProcess(viewController: self,
                                                                       delegate: self,
                                                                       token: token)
        } else {
            // 取得したトークンを扱う
            self.onSuccess(token)
        }
    case .failure(let error):
            // エラー処理
        }
    }
}

認証が終了し、リダイレクト URL によって ThreeDSecureProcessHandler.completeThreeDSecureProcess(url:) が呼び出されることで、SDK は認証フローの終了を検知します。 ThreeDSecureProcessHandlerDelegate をアプリの ViewController に適合し、 threeDSecureProcessHandlerDidFinish(_:status:) で受け取る ThreeDSecureProcessStatus に応じてトークン作成処理を完了させてください。

public func threeDSecureProcessHandlerDidFinish(_ handler: ThreeDSecureProcessHandler,
                                                status: ThreeDSecureProcessStatus) {
    switch status {
    case .completed:
        // 3DSの処理を完了する
        completeTokenTds()
    case .canceled:
        // UI更新など
    default:
        break
    }
}

ThreeDSecureProcessStatus が .completed の場合、先に取得した Token を利用して認証済みの Token を取得できます。 APIClient.finishTokenThreeDSecure(tokenId:completion:) を利用して再取得を行ってください。

let completion: (Result<Token, APIError>) -> Void = { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .success(let token):
        self.onSuccess(token)
    case .failure(let error):
        self.onFailure(error)
    }
}
if let token = self.pendingToken {
    APIClient.shared.finishTokenThreeDSecure(tokenId: token.identifer, completion: completion)
}

以上でトークン作成時に 3Dセキュアを要求する iOS の実装は完了です。

payjp-androidの場合

リダイレクトURLを登録する

管理画面で指定したリダイレクト URL と識別子を登録します。

Payjp.init(PayjpConfiguration.Builder(YOUR_PUBLIC_KEY)
    .setThreeDSecureRedirectName("mobileapp") // URLではなく識別子を登録します
    .build())

AndroidManifest.xmlにIntentFilterを追加する

アプリの AndroidManifest.xml に管理画面で指定したリダイレクト URL を追加します。アクティビティとして jp.pay.android.verifier.ui.PayjpThreeDSecureStepActivity を追加し、IntentFilter を設定します。

たとえば、リダイレクト URL が my-app://tds/complete の場合、以下のように記述します。

<activity android:name="jp.pay.android.verifier.ui.PayjpThreeDSecureStepActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="my-app" android:host="tds" android:path="complete" />
    </intent-filter>
</activity>

カード所有者を3Dセキュアに誘導する

カードフォーム画面を利用している場合

Payjp.cardForm().start() を利用してカードフォーム画面を呼び出している場合は、SDK が自動的にハンドリングするため、カードフォーム開始時に 3Dセキュアの実施を指定するのみです。

Payjp.cardForm().start(
    activity = this,
    face = PayjpCardForm.FACE_MULTI_LINE,
    extraAttributes = arrayOf(ExtraAttribute.Email()),
    useThreeDSecure = true,
)

それ以外の場合

3Dセキュアの認証を実施するには、 createToken の引数に useThreeDSecure = true を指定します。

createToken によって取得した Token の Token.card.threeDSecureStatus が UNVERIFIED のとき、3Dセキュアの認証を開始してください。

まず、 Token.retrieveId() で得られる TokenId を、 Payjp.verifier().startThreeDSecureFlow(TokenId, Activity) の第1引数に渡します。

これにより、SDK はブラウザを開きカード所有者を認証画面へと誘導します。認証が完了、失敗、またはキャンセルによって終了するまで、SDK によるトークン作成処理は中断状態となります。

以下の例は PayjpCardFormFragment#createToken を利用した場合です。

cardFormFragment.createToken(useThreeDSecure = true).enqueue(object : Task.Callback<Token> {
    override fun onSuccess(data: Token) {
        if (data.card.threeDSecureStatus == ThreeDSecureStatus.UNVERIFIED) {
             Payjp.verifier()
                .startThreeDSecureFlow(data.retrieveId(), this@SampleActivity)
        } else {
            sendTokenToServer(token: data)
        }
    }

    override fun onError(throwable: Throwable) {
        updateUI(throwable)
    }
})

認証が終了すると、Payjp.verifier().startThreeDSecureFlow(TokenId, Activity) の第2引数に渡した呼び出し元の Activity(または Fragment)で onActivityResult に結果を受け取ります。

Payjp.verifier().handleThreeDSecureResult(requestCode, callback) を使って認証フローの結果を取り出し、コールバックで渡される PayjpThreeDSecureResult に応じて処理を再開してください。 PayjpThreeDSecureResult#isSuccess() が true の場合は認証フローが完了していることを表します。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    Payjp.verifier().handleThreeDSecureResult(requestCode) { result ->
        if (result.isSuccess()) {
            this.onCompleteTds(result)
        } else {
            Toast.makeText(this, "3-D Secure canceled.", Toast.LENGTH_SHORT).show()
        }
    }
}

3Dセキュアの認証に成功した場合、Payjp.verifier().completeTokenThreeDSecure(result) で認証済みの Token を取得します。

if (!result.isSuccess()) {
  return
}
Payjp.token().completeTokenThreeDSecure(result)
    .enqueue(object : Task.Callback<Token> {
        override fun onSuccess(data: Token) {
            Log.i("PAY.JP", "token => $data")
        }
        override fun onError(throwable: Throwable) {
            Log.e("PAY.JP", "failure getting token", throwable)
        }
    })

以上でトークン作成時に 3Dセキュアを要求する Android の実装は完了です。

payjp-react-native-pluginの場合

カードフォーム画面を利用する方法以外はサポートしていません。

各プラットフォームごとにリダイレクトの設定をする

iOS では、payjp-ios同様、XcodeでURLスキームを設定します。 Android では、payjp-android同様、AndroidManifest.xmlにIntentFilterを追加します。

リダイレクトURLを登録する

管理画面で設定したリダイレクト URL と識別子を登録します。 PayjpCore#init の引数となるオブジェクトに threeDSecureRedirect というキーで下記のように URL と識別子を指定してください。

PayjpCore.init({
    publicKey: YOUR_PUBLIC_KEY,
    threeDSecureRedirect: {
        url: "jp.pay.example://tds/finish",
        key: "mobileapp",
    },
});

カードフォーム開始時に 3Dセキュアの実施を指定する

PayjpCardForm.startCardForm() の引数となる CardFormOption オブジェクトに useThreeDSecure というキーで true を指定してください。

PayjpCardForm.startCardForm({
    useThreeDSecure: true
});

payjp-flutter-pluginの場合

カードフォーム画面を利用する方法以外はサポートしていません。

各プラットフォームごとにリダイレクトの設定をする

iOS では、payjp-ios同様、XcodeでURLスキームを設定します。 Android では、payjp-android同様、AndroidManifest.xmlにIntentFilterを追加します。

リダイレクトURLを登録する

管理画面で設定したリダイレクト URL と識別子を登録します。 Payjp#init の引数となるオブジェクトに threeDSecureRedirect というキーで下記のように URL と識別子を指定してください。

Future<void> _initPayjp() async {
  await Payjp.init(
    publicKey: YOUR_PUBLIC_KEY,
    threeDSecureRedirect: PayjpThreeDSecureRedirect(
      url: 'jp.pay.example://tds/finish', key: 'mobileapp'));
}

カードフォーム開始時に 3Dセキュアの実施を指定する

Payjp#startCardForm の引数 useThreeDSecure に true を指定してください。

await Payjp.startCardForm(
        onCardFormCanceledCallback: _onCardFormCanceled,
        onCardFormCompletedCallback: _onCardFormCompleted,
        onCardFormProducedTokenCallback: _onCardFormProducedToken,
        useThreeDSecure: true);

まとめ

モバイル SDK を利用して 3Dセキュアを導入する手順は以下のとおりです。

  1. 管理画面でリダイレクト URL と識別子を設定する
  2. 各 SDK ごとに必要な設定を行う
    • iOS の場合は Xcode で URL スキームを設定
    • Android の場合は AndroidManifest.xml に IntentFilter を追加
  3. SDK の初期化時にリダイレクト URL と識別子を登録する
  4. カードフォーム画面開始時(またはトークン作成時)に、3Dセキュアの実施を指定する(管理画面の設定によっては不要)
  5. トークン作成後、3Dセキュアが必要な場合は SDK の機能を使ってカード所有者を認証フローに誘導する
  6. 認証後のリダイレクトを受け取り、SDK の機能で認証済みのトークンを取得する

これらの手順に沿って実装を行うことで、モバイルアプリでも安全なクレジットカード決済を提供できます。 セキュリティ強化のため、ぜひ 3Dセキュアの導入をご検討ください。

リファレンス