PAY.JP iOS SDKは、iOS アプリに PAY.JP を導入するための SDK です。
この SDK が提供する「カード情報のトークン化」機能を利用することで、事業者さまのサーバーでカード情報を扱うことなく、安全に支払い処理をおこなうことができます。
ここでは、アプリに SDK を組み込み、クレジットカード情報をトークン化する方法について説明します。
以下のサンプルコードは Swift で記述されています。Objective-C のサンプルコードについては、GitHub のリポジトリをご覧ください。
payjp/payjp-ios: PAY.JP iOS SDK
なお、SDK を使った Apple Pay の導入方法については、次のドキュメントをご参照ください。
インストール
以下の方法をサポートしています。
Swift Package Manager を使う場合
https://github.com/payjp/payjp-ios.git を追加してください。
Carthage を使う場合
github "payjp/payjp-ios"
CocoaPods を使う場合
pod 'PAYJP'
SDKの初期化
まず、UIApplicationDelegate
を継承したクラスで、PAY.JP の公開鍵を設定し、SDK を初期化します。
以下のサンプルコードではテストモードの公開鍵を設定しています。
import PAYJP
class AppDelegate: UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
PAYJPSDK.publicKey = "pk_test_0383a1b8f91e8a6e3ea0e2a9"
PAYJPSDK.locale = Locale.current
return true
}
}
アプリに組み込む方法の選択
SDK をアプリに組み込み、トークンを取得する方法は複数用意されており、事業者さまの要件に合わせて選択いただけます。
1. 用意されたカードフォーム画面を利用する(推奨)
これから SDK の組み込みを始める場合は、この方法を推奨します。
SDK が提供するフォーム画面を表示し、カード情報のトークン化をフォーム画面内で SDK が行います。アプリは生成されたトークンをサーバーに送信するだけなので、最も簡単な方法です。
フォームのスタイルは FormStyle
クラスを使ってカスタマイズできます。
2. アプリで用意した画面にカードフォームを組み込む
CardFormTableStyledView
、CardFormLabelStyledView
、CardFormDisplayStyledView
のいずれかをアプリの画面に追加する方法です。この場合もカードフォームは SDK が提供するので、アプリがカード情報を扱う必要はありません。
追加の支払い情報などを合わせて表示したいなど、1 の方法では実現できないような画面が必要な場合におすすめです。
3. SDKのUIを使わずに直接トークンを生成する
1、2 いずれの方法でも要件を満たせない場合は、カード情報から直接トークンを生成することも可能です。
1. カードフォーム画面を利用する場合(推奨)
STEP 1: フォーム画面を開始する
カードフォーム画面のフローを開始するには、CardFormViewController#createCardFormViewController
を呼び出します。正常に画面が表示されない場合は、SDKの初期化に問題がある可能性があります。
// ViewControllerから呼び出す
let style = FormStyle(
labelTextColor: .black,
inputTextColor: .black,
tintColor: .blue)
let cardFormViewController = CardFormViewController.createCardFormViewController(
style: style, delegate: self, viewType: .labelStyled
)
self.navigationController?.pushViewController(
cardFormViewController, animated: true
)
このとき呼び出す CardFormViewController#createCardFormViewController
に渡すことのできる引数は以下です。
引数名 | 説明 |
---|---|
style |
フォームのカラーや見た目を変更する場合は FormStyle を指定して上書きできます。 |
tenantId |
PAY.JP Platform の Marketplace 型を利用している場合にテナント ID を指定できます。( トークン作成時のテナント ID について ) |
delegate |
カードフォームの結果を受け取るための CardFormViewControllerDelegate を指定します。 |
viewType |
カードフォームの表示タイプを切り替えます。デフォルトは .labelStyled が適用されます。 |
extraAttributes |
カードフォームに追加の属性項目を設定できます。デフォルトはメールアドレスと電話番号が表示されます。 |
カードフォームの見た目を変更する
カードフォーム画面は 3 種類の表示タイプを CardFormViewType
で指定できます。
フォームやボタンの色は FormStyle
で変更できます。(見た目を変更する)
CardFormViewType | スクリーンショット |
---|---|
.tableStyled |
|
.labelStyled |
|
.displayStyled |
追加の属性項目を設定する
引数 extraAttributes
に ExtraAttribute
の配列を渡すことで、追加の属性項目を変更できます。
属性の種類はメールアドレス(ExtraAttributeEmail
)と電話番号(ExtraAttributePhone
)の2種類があり、入力した内容はカードオブジェクトにセットされ、 3Dセキュア認証実施時に送信されます。
STEP 2: フォームの結果を受け取る
カードフォーム画面を呼び出した ViewController を CardFormViewControllerDelegate
に適合させ、cardFormViewController(_:didProduced:completionHandler:)
を実装します。
func cardFormViewController(_: CardFormViewController,
didProduced token: Token,
completionHandler: @escaping (Error?) -> Void) {
print("token = \(token)")
}
STEP 3: 生成されたトークンをサーバに送信する
通常、カード情報から生成されたトークンは事業者さまのサーバーに送信し、顧客データや支払いデータを作成する等の処理を行います。 トークン取得後の一連の処理が何らかの事由(たとえばネットワークエラーなど)で中断した場合、カードフォーム画面が終了していると、再度カードフォーム画面を表示し顧客に入力を求めなければなりません。
そのため、トークン取得時に呼び出されるデリゲートを通じて、フォームを表示した状態でアプリ側からエラーメッセージを渡せる仕組みを提供しています。
STEP 2 で実装した cardFormViewController(_:didProduced:completionHandler:)
の中で、トークンをサーバーに送信するなどの処理を行います。このメソッドはバックグラウンドで実行されるため、UI の操作を直接実行しないよう注意してください。
処理の最後に引数の completionHandler:
を呼び出し、カードフォームに結果を反映します。
トークンの処理に問題があった場合は、completionHandler:
にカードフォームにエラーとして表示するメッセージを渡します。問題なく完了した場合は引数を nil
として呼び出すことで、カードフォームを終了します。
func cardFormViewController(_: CardFormViewController,
didProduced token: Token,
completionHandler: @escaping (Error?) -> Void) {
// サーバにトークンを送信
apiService.saveToken(withToken: token) { (error) in
if let error = error {
completionHandler(error)
} else {
completionHandler(nil)
}
}
}
カードフォームはその結果に応じて CardFormResult
を返して終了します。
結果を受け取るには、CardFormViewControllerDelegate#cardFormViewController(_:didCompleteWith:)
を実装します。
func cardFormViewController(_: CardFormViewController,
didCompleteWith result: CardFormResult) {
switch result {
case .cancel:
// カードフォームをキャンセルした場合の処理
case .success:
// トークンの保存に成功したらカードフォームを終了する
DispatchQueue.main.async { [weak self] in
self?.navigationController?.popViewController(animated: true)
}
}
}
以上が、カードフォーム画面を使ったアプリへの組み込みの基本的な流れとなります。
2. カードフォームをアプリの画面に組み込む場合
SDK では、事業者さまのアプリにそのまま組み込むことができるカードフォームを提供しています。事業者さまはカードフォームをアプリに組み込むことで、カード情報を取り扱うことなく、SDK によって安全にトークンを取得できます。また、入力補完や入力ミスの表示など、カード情報の入力に適切な顧客体験を提供します。
サンプルアプリの例(色や TextField のスタイルはカスタマイズ可能です)
STEP 1: フォームの組み込み
カードフォームを表示するには、CardFormTableStyledView
、CardFormLabelStyledView
、CardFormDisplayStyledView
のいずれかを利用します。CocoaPods を使用する場合は Storyboard に View を配置して利用できますが、Carthage を使用する場合は Storyboard での利用ができません。そのため、コード上で View を addSubView する形で実装します。
CocoaPods をご利用の方は example-objc
、Carthage をご利用の方は example-swift
のサンプルコードを参照ください。
STEP 2: フォームの送信
CardFormAction#createToken
は Result<Token, Error>
を返し、生成したトークンまたはエラーを取得できます。
エラー項目についてはErrorを参照ください。
func onClickSubmit() {
cardFormView.createToken() { result in
switch result {
case .success(let token):
print("[token] => \(token)")
case .failure(let error):
print("[failure creating token] \(error)")
}
}
}
STEP 3: フォームのバリデーション
カードフォームは入力内容が適切か入力のたびに検証します。
検証の結果のみを得るには CardFormAction#isValid
を利用します。
CardFormAction#validateCardForm
は暗黙の入力エラーを表示し、検証結果を返します。たとえば、送信ボタンを押したタイミングで入力の検証を行い、未入力の項目があればエラーメッセージを表示したい場合はこちらを利用します。
また、検証結果の通知を受け取りたい場合は、ViewController を CardFormViewDelegate
に適合させ、formInputValidated(in:isValid:)
を実装します。
func formInputValidated(in cardFormView: UIView, isValid: Bool) {
createTokenButton.isEnabled = isValid
}
フォームをカスタマイズする
クレジットカード情報をカメラで読み取る
カメラでクレジットカードの券面をスキャンし、自動で入力する機能を追加できます。
この機能を利用するには、Card IOのiOS SDKをインストールします(最新のバージョンを利用してください)。
pod 'CardIO'
iOS アプリでカメラを利用する場合、なぜ必要なのかをユーザーに明示する必要があります。Info.plist
にNSCameraUsageDescriptionというキーで説明を追加します。
これでカードフォームに番号読み取りのカメラアイコンが表示されます。
カード保有者名の入力を非表示にする
カード保有者名は @IBInspectable
で定義している isHolderRequired
を Storyboard から設定、またはコード上から値をセットすることで表示/非表示の切り替えができます(デフォルトは表示)。
コード上からセットする場合は、CardFormStylable#setCardHolderRequired
を利用してください。
追加の属性項目を変更する
ExtraAttribute
で入力された情報は3Dセキュアの認証情報として使用されます。
3Dセキュアは2025年3月末までの運用開始が義務化されており、その一つとしてこの追加項目が求められます。
詳細は3Dセキュア認証 - 導入の義務化およびPAY.JPにおける3D セキュア - 3Dセキュア認証における追加項目をご覧ください。
カードフォームに表示される追加の属性項目は ExtraAttribute
の配列を CardFormViewController
または組み込み用の View クラスに渡すことで変更できます。
CardFormViewController
の場合は CardFormViewController#createCardFormViewController
の引数の extraAttributes
を設定します。
組み込み用の View クラスの場合は CardFormAction#apply(extraAttributes:)
をユーザーが画面を操作する前に呼び出すようにしてください。
- メールアドレスと電話番号両方を表示する場合は、いずれかの入力が必須となります。
- メールアドレスか、電話番号どちらかのみ表示する場合は、表示した項目の入力が必須となります。
- 初期値を設定した場合はフォームに入力された状態で表示され、ユーザーは入力内容を編集して送信できます。
- いずれの項目も表示しない場合は、空の配列を渡すようにしてください。
たとえば、メールアドレスのみを初期値を設定して表示する場合は、以下のサンプルコードのように指定します。
// アプリを利用しているユーザーのメールアドレスを初期値として表示する
let extraAttributes = [
ExtraAttributeEmail(preset: user.email)
// ExtraAttribute.Phone() // 電話番号の入力は要求しない
]
let cardForm = CardFormViewController.createCardFormViewController(delegate: self, extraAttributes: attributes)
present(cardForm, animated: true)
完全なサンプルコードは GitHub リポジトリ でも公開していますのでご参照ください。
見た目を変更する
カードフォームの種類は、UITableView に適した CardFormTableStyledView
、ラベル付きの CardFormLabelStyledView
、カード表示ありの CardFormDisplayStyledView
の 3 種類があります。
共通のインターフェースを持っているため、基本的な機能は同じです。
フォームをカスタマイズするには CardFormStylable#apply
を利用してください。FormStyle
クラスで UIColor
を設定することで、各コンポーネントの色を変更できます。
let style = FormStyle(
labelTextColor: .black,
inputTextColor: .black,
tintColor: .blue,
inputFieldBackgroundColor: .white)
cardFormView.apply(style: style)
コンポーネント | FormStyle |
---|---|
Label 文字の色 | labelTextColor |
TextField 入力文字の色 | inputTextColor |
TextField エラーメッセージの色 | errorTextColor |
TextField カーソルの色 | tintColor |
TextField 背景の色 | inputFieldBackgroundColor |
Button 背景の色 | submitButtonColor |
Highlight ハイライトの色 | highlightColor |
3. カードフォームを利用せずトークン化する場合
トークンを生成するには以下のカード情報が必要です。
- クレジットカード番号(例:
4242424242424242
) - CVC(例:
123
) - 有効期限(月)(例:
02
) - 有効期限(年)(例:
2020
) - (任意)カード保有者名(例:
PAY TARO
)
APIClient#createToken
は上記のカード情報を引数に呼び出すことで、Result<Token, APIError>
を返し、生成したトークンまたはエラーを取得できます。
エラー項目についてはErrorを参照ください。
payjpClient.createToken(
with: "4242424242424242",
cvc: "123",
expirationMonth: "02",
expirationYear: "2020",
name: "PAY TARO") { result in
switch result {
case .success(let token):
print("token_id is \(token.identifier)")
case .failure(let error):
print(error.localizedDescription)
}
}
生成済みのトークン情報を取得する
トークン ID を利用して、すでに生成済みのトークンの情報を取得するには、APIClient#getToken
を呼び出します。
payjpClient.getToken(with: tokenId) { result in
switch result {
case .success(let token):
print("token_id is \(token.identifier)")
case .failure(let error):
print(error.localizedDescription)
}
}
トークン情報を利用する
生成したトークンは、支払いをはじめとする各種処理に利用できます。
トークンオブジェクト(PAYJP.Token
)には、以下のような情報が含まれます。
private extension Token {
func print() {
print("id=\(identifier)")
print("card.id=\(card.identifier)")
print("card.last4=\(card.last4Number)")
print("card.exp=\(card.expirationMonth)/\(card.expirationYear)")
print("card.name=\(card.name ?? "nil")")
}
// id=tok_5ca06b51685e001723a2c3b4aeb4
// card.id=car_e3ccd4e0959f45e7c75bacc4be90
// card.last4=4242
// card.exp=2/2020
// card.name=PAY TARO
}
トークンオブジェクト(PAYJP.Token
)の詳細については、APIリファレンスを参照ください。
Error
トークンのリクエストが失敗した場合、Result
に格納される Error
として APIError
と LocalError
があります。
これらのエラーは errorCode
に応じて以下のように分類されます。
Error | errorCode | 説明 |
---|---|---|
.invalidApplePayToken |
0 | Apple Pay のトークン不正エラー。userInfo にキー PAYErrorInvalidApplePayTokenObject で対象のトークンが格納されています。 |
.systemError |
1 | URLSession に由来するエラー。userInfo にキー PAYErrorSystemErrorObject で元のエラーが格納されています。 |
.invalidResponse |
2 | レスポンスの不正エラー。userInfo にキー PAYErrorInvalidResponseObject で HTTP レスポンスが格納されています。 |
.serviceError |
3 | サーバーエラー。userInfo にキー PAYErrorServiceErrorObject でエラーレスポンスが格納されています。エラーレスポンスの type と code を確認し、API リファレンスのErrorを参照ください。 |
.invalidJSON |
4 | JSON データの不正エラー。userInfo にキー PAYErrorInvalidJSONObject で JSON データ、PAYErrorInvalidJSONErrorObject でエラーが格納されています。 |
.invalidFormInput |
5 | 入力値不正エラー。カードフォームの入力値に問題がある場合にこのエラーが返されます。 |
.requiredThreeDSecure |
6 | 3D セキュア認証の要求エラー。userInfo にキー PAYErrorRequiredThreeDSecureIdObject で 3D セキュアトークンが格納されています。 |
.rateLimitExceeded |
7 | レートリミット超過エラー。「混雑のため、一時的に利用が制限されています。時間を空けて再度お試しください。」など、時間をあけた再試行を提案してください。 |
トークン作成リクエストのバーストを抑制する
カード情報入力フォームがユーザーの連打などで意図せず連続でトークンの作成をリクエストできてしまうと、レートリミットの制限に達する可能性があります。
SDK が提供するカードフォーム画面はあらかじめ対策がなされていますが、カードフォーム画面を利用する以外の方法でアプリに組み込む場合は、PAYTokenOperationStatus
を利用して、ユーザーによる連打などで意図せず Token の作成を連続リクエストしないよう抑制できます。
ステータスの更新は NotificationCenter
経由で通知されます。
PAYTokenOperationStatus
が .acceptable
のときにトークンを作成できるよう、UI を更新してください。
override func viewDidLoad() {
// ...
NotificationCenter.default.addObserver(self,
selector: #selector(handleTokenOperationStatusChange(notification:)),
name: .payjpTokenOperationStatusChanged,
object: nil)
}
@objc private func handleTokenOperationStatusChange(
notification: Notification
) {
if let value = notification.userInfo?[
PAYNotificationKey.newTokenOperationStatus
] as? Int,
let newStatus = TokenOperationStatus.init(rawValue: value) {
self.toggleButton(enabled: newStatus == .acceptable)
}
}
ソースコード・サンプルコード
SDK は GitHub で公開されています。 SDK のソースコードのほか、Swift/Objective-C のサンプルコードを参照できます。