Stripeの以下のドキュメントの記述にそって、Oracle APEXに合わせた実装を行います。
支払いページを作成する
Payment Intentの作成には、以下のREST APIを呼び出します。
カード番号の入力フォームはPayment Elementとして説明されています。
作成したアプリケーションは以下のように動作します。
最後にREST APIを呼び出してPayment Intentを表示しています。実際に決済の成功や失敗を確認するにはWebhookを実装するのが一般的だと思いますが、本記事ではWebhookの作成は取り扱っていません。通常Webhookは、APEXのアプリケーションではなく、ORDSのRESTサービスとして実装します。
以下より実装について説明します。
アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。名前はStripeとします。
アプリケーションの作成をクリックします。
以下より実装について説明します。
アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。名前はStripeとします。
アプリケーションの作成をクリックします。
パブリックキーは置換文字列をSTRIPE_PUBLISHABLE_KEY、置換値としてpk_test_で始まる(テスト用の)公開可能キーを設定します。
APIのエンドポイントは置換文字列としてSTRIPE_ENDPOINT、置換値としてhttps://api.stripe.comを設定します。
ワークスペース・ユーティリティのWeb資格証明を開きます。
StripeのAPIは、ベアラーとしてシークレットキーを渡すことで認証できます。認証タイプにHTTPヘッダーを選択し、資格証明名はAuthorization、資格証明シークレットとして、文字列Bearerで始めて空白で区切り、シークレットキーの値を設定します。
Stripeによるカード情報入力フォームを表示するページを作成します。
ページを初期化するプロセスを作成します。
上にあるカードの所有者を入力するページ・アイテムと下にあるボタンが、カード情報を入力するリージョンに近過ぎたため、テンプレート・オプションのTop MarginとBottom MarginをLargeに変更しています。
Bearer シークレットキー
Web資格証明の名前はStripe CRED、静的IDはSTRIPE_CREDとして、Web資格証明を作成します。URLに対して有効はhttps://api.stripe.comを設定します。
以上で作成をクリックします。
Web資格証明としてStripe CRED(静的IDはSTRIPE_CRED)が作成されました。
ページの作成をクリックします。
空白ページを選択します。
ページ番号を2(ページ・アイテム名に現れるため2とします)、名前をPay、ページ・モードとしてドロワーを選択します。
ページの作成をクリックします。
ページの作成をクリックします。
ページPayが作成されました。
ホーム・ページでは決済金額を入力し、確定ボタンを押します。確定ボタンを押すとStripeが提供するカード情報を入力するフォームを実装したドロワーが開き、そこで決済を実行します。決済を実行すると、ドロワーが閉じます。
ホーム・ページでは決済金額を入力し、確定ボタンを押します。確定ボタンを押すとStripeが提供するカード情報を入力するフォームを実装したドロワーが開き、そこで決済を実行します。決済を実行すると、ドロワーが閉じます。
ページ・デザイナでホーム・ページを開きます。
金額を入力するページ・アイテムP1_AMOUNTを作成します。
タイプは数値フィールド、ラベルはAmountとします。
決済金額を確定するボタンCONFIRMを作成します。
ラベルはConfirm、外観のホットをオン、テンプレート・オプションのWidthとしてStretchを選択します。動作のアクションはデフォルトのページの送信から変更しません。
作成したPayment Intent IDを保持するページ・アイテムP1_PAYMENT_INTENT_IDとClient Secretを保持するページ・アイテムP1_CLIENT_SECRETを作成します。双方ともタイプは非表示です。
Payment Intent IDはPayment Intentを表示する際に使用し、決済の確定では使用しません。APIリファレンスには、シークレットキーとPayment Intent IDを組み合わせてPayment Intentを取得する、または、公開可能キーとClient Secretを組み合わせてPayment Intentを取得する2通りの手順があるとのことです。Client Secretは決済の確定に使用します。
セッション・ステートのストレージとしてセッションごと(永続)を選択します。決済の確定はPL/SQLで実行します。その際にページ・アイテムに設定されたPayment Intent IDは、ページ遷移の後でも値を維持するようにします。
Payment Intentを表示するリージョンを作成します。
識別のタイトルはPayment Intent、タイプとして動的コンテンツを選択します。
ソースのCLOBを返すPL/SQLファンクション本体として以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_response clob; | |
begin | |
if :P1_PAYMENT_INTENT_ID is null then | |
return ''; | |
end if; | |
apex_web_service.clear_request_headers(); | |
l_response := apex_web_service.make_rest_request( | |
p_url => :STRIPE_ENDPOINT || '/v1/payment_intents/' || :P1_PAYMENT_INTENT_ID | |
,p_http_method => 'GET' | |
,p_credential_static_id => 'STRIPE_CRED' | |
); | |
return l_response; | |
end; |
ページ・アイテムP1_PAYMENT_INTENT_IDはセッション・ステートとして保持されているため、送信するページ・アイテムへの設定は不要です。
このリージョンをリフレッシュするボタンREFRESHを作成します。
ラベルはRefresh、動作のアクションとして動的アクションで定義を選択します。
ボタンREFRESHの動的アクションを作成します。
識別の名前はonClick REFRESH、タイミングのイベントはボタンのデフォルトであるクリックです。
TRUEアクションとしてリフレッシュを選択します。影響を受ける要素の選択タイプはリージョン、リージョンとしてPayment Intentを選択します。
このページを初期化するボタンCLEARを作成します。
ラベルはClear、動作のアクションはページの送信です。レイアウトの新規行の開始はオフにし、ボタンREFRESHの右隣に配置します。
プロセス・ビューを開き、ボタンを押した時に実行されるプロセスを作成します。
ボタンCONFIRMを押した時にPayment Intentを作成します。
プロセスの識別の名前はCreate Payment Intent、タイプとしてコードを実行を選択します。ソースのPL/SQLコードとして以下を記述します。Payment Intentを作成するための最低限の処理です。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
declare | |
l_response clob; | |
l_response_json json_object_t; | |
e_create_charge_failed exception; | |
begin | |
apex_web_service.set_request_headers('Content-Type', 'application/x-www-form-urlencoded'); | |
l_response := apex_web_service.make_rest_request( | |
p_url => :STRIPE_ENDPOINT || '/v1/payment_intents' | |
,p_http_method => 'POST' | |
,p_parm_name => apex_util.string_to_table('amount:currency:automatic_payment_methods[enabled]') | |
,p_parm_value => apex_util.string_to_table(:P1_AMOUNT || ':jpy:true') | |
,p_credential_static_id => 'STRIPE_CRED' | |
); | |
apex_debug.info(l_response); | |
if apex_web_service.g_status_code <> 200 then | |
apex_debug.info(l_response); | |
raise e_create_charge_failed; | |
end if; | |
l_response_json := json_object_t(l_response); | |
:P1_PAYMENT_INTENT_ID := l_response_json.get_string('id'); | |
:P1_CLIENT_SECRET := l_response_json.get_string('client_secret'); | |
end; |
サーバー側の条件のボタン押下時としてCONFIRMを設定します。
識別の名前はClear Session State、タイプはセッション・ステートのクリア、設定のタイプとしてClear all Items on the Current Pageを選択します。
サーバー側の条件のボタン押下時としてCLEARを設定します。
ブランチを作成しプロセスの後に実行するように配置します。
識別の名前はOpen Drawer、動作のタイプとしてページまたはURL(リダイレクト)を選択します。
サーバー側の条件のボタン押下時としてCONFIRMを設定します。
ターゲットは、タイプがこのアプリケーションのページ、ページは2です。アイテムの設定として、ページ・アイテムP2_CLIENT_SECRETに&P1_CLIENT_SECRET.の値が渡るようにします。
以上で、ボタンをCONFIRMをクリックするとPayment Intentが作成され、そのClient Secretを(ページ・アイテムP2_CLIENT_SECRETへの)引数としてドロワーが開くようになります。
ページ・デザイナでページPayを開きます。
ページ・プロパティのJavaScriptのファイルURLとして、Stripeが提供しているJavaScriptライブラリを指定します。
https://js.stripe.com/v3/
ファンクションおよびグローバル変数の宣言に以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const stripe = Stripe('&STRIPE_PUBLISHABLE_KEY.'); | |
const elements = stripe.elements(); | |
const card = elements.create("card", { | |
hidePostalCode: true, | |
style: { | |
base: { | |
color: "#32325d" | |
} | |
} | |
}); | |
document.addEventListener('DOMContentLoaded', async function () { | |
card.mount("#card-element"); | |
card.on('change', ({error}) => { | |
let displayError = document.getElementById('card-errors'); | |
if (error) { | |
displayError.textContent = error.message; | |
} else { | |
displayError.textContent = ''; | |
} | |
}); | |
}); |
カード情報の入力フォームを生成しています。
クレジットカード情報を入力するフォームを生成するリージョンを作成します。
Content Bodyの直下にタイプが静的コンテンツのリージョンを作成します。
識別のタイトルはpayment-form、余計な装飾を省くため外観のテンプレートとしてBlank with Attributes (No Grid)を選択します。詳細の静的IDとしてpayment-formを設定します。このリージョンは実施的に以下のDIV要素を生成します。
<div id="payment-form">....</div>
Stripeのチュートリアルではidがpayment-formの要素はFORMです。Oracle APEXが生成したページにFORM要素がすでに含まれているためか、<form id="payment-form">では動作しません。そのため、代わりにDIV要素を作成しています。
リージョンpayment-formにClient Secretを保持するページ・アイテムを作成します。
識別の名前はP2_CLIENT_SECRET、タイプとして非表示を選択します。レイアウトのリージョンとしてpayment-formを選択します。セッション・ステートのストレージはリクエストごと(メモリーのみ)とします。
クレジット・カードの所有者を入力するページ・アイテムを作成します。
識別の名前はP2_HOLDER_NAME、タイプはテキスト・フィールド、ラベルはHolder Nameとします。レイアウトのリージョンとしてpayment-formを選択します。
リージョンpayment-formの子リージョンを作成します。
識別のタイトルはcard-element、タイプは静的コンテンツです。レイアウトの親リージョンとしてpayment-formを指定します。このリージョンも余計な装飾を省くため、外観のテンプレートとしてBlank with Attributes (No Grid)を選びます。詳細の静的IDとしてcard-elementを設定します。
エラーを表示するリージョンを作成します。
識別のタイトルはcard-errors、タイプは静的コンテンツです。レイアウトの親リージョンとしてpayment-formを指定します。このリージョンも余計な装飾を省くため、外観のテンプレートとしてBlank with Attributes (No Grid)を選びます。詳細の静的IDとしてcard-errorsを設定します。
決済を確定するボタンPAYを作成します。
ラベルはPay、レイアウトのリージョンとしてpayment-form、外観のホットをオンにし、テンプレート・オプションのWidthにStretchを選択します。動作のアクションとして動的アクションで定義を選択します。
ボタンPAYに動的アクションを作成します。
識別の名前はonClick PAYとします。タイミングのイベントはボタンのデフォルトであるクリックです。
TRUEアクションとしてJavaScriptコードの実行を選択し、設定のコードに以下を記述します。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
this.browserEvent.preventDefault(); | |
stripe.confirmCardPayment(apex.items.P2_CLIENT_SECRET.value, { | |
payment_method: { | |
card: card, | |
billing_details: { | |
name: apex.items.P2_HOLDER_NAME.value | |
} | |
} | |
}).then(function(result) { | |
if (result.error) { | |
// Show error to your customer (for example, insufficient funds) | |
console.log(result.error.message); | |
} else { | |
// The payment has been processed! | |
if (result.paymentIntent.status === 'succeeded') { | |
apex.navigation.dialog.close(true); | |
// Show a success message to your customer | |
// There's a risk of the customer closing the window before callback | |
// execution. Set up a webhook or plugin to listen for the | |
// payment_intent.succeeded event that handles any business critical | |
// post-payment actions. | |
} | |
} | |
}); |
カード決済が確定するとドロワーを閉じます。
以上でStripeのPayment Intentを使ったの決済サービスの実装は完了です。アプリケーションを実行すると、この記事の先頭のGIF動画のように動作します。
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完