如何将Twilio客户电话号码同步为Stripe客户信息?
问题:Twilio电话支付对接Stripe时,如何将客户手机号关联为Stripe客户?
我正在用Twilio实现电话自动支付并对接Stripe,目前所有支付在Stripe里都显示为未知/访客,没法通过手机号识别用户、查看购买记录。虽然已经在Twilio Studio的「支付描述」里填了{{trigger.call.From}},但这没彻底解决问题,需要把客户的电话号码作为Stripe的「客户」信息传入。
当前Flow代码
{"description": "A New Flow","states": [{"name": "Trigger","type": "trigger","transitions": [{"next": "received_message_forward","event": "incomingMessage"},{"next": "gather_1","event": "incomingCall"},{"event": "incomingConversationMessage"},{"event": "incomingRequest"},{"event": "incomingParent"}],"properties": {"offset": {"x": 0,"y": 0}}},{"name": "gather_1","type": "gather-input-on-call","transitions": [{"next": "function_1","event": "keypress"},{"event": "speech"},{"next": "gather_1","event": "timeout"}],"properties": {"voice": "man","speech_timeout": "auto","offset": {"x": 30,"y": 270},"loop": 1,"finish_on_key": "#","say": "Welcome and thank you for calling the donation line. Please enter the amount you wish to donate, then press pound. To enter with cents use the star key. for example to enter $18.50, press one eight star five zero.","language": "en-US","stop_gather": true,"gather_language": "en","profanity_filter": "true","timeout": 5}},{"name": "function_1","type": "run-function","transitions": [{"next": "confirm_donation_amount","event": "success"},{"event": "fail"}],"properties": {"offset": {"x": -60,"y": 530},"parameters": [{"value": "{{widgets.gather_1.Digits}}","key": "amount"}],"url": "https://sand-pointer-5043.twil.io/pay_from_exchange"}},{"name": "say_play_2","type": "say-play","transitions": [{"event": "audioComplete"}],"properties": {"voice": "man","offset": {"x": -60,"y": 840},"loop": 1,"say": "You entered ${{widgets.function_1.body}}","language": "en-US"}},{"name": "pay_1","type": "capture-payments","transitions": [{"next": "say_play_3","event": "success"},{"next": "say_play_4","event": "maxFailedAttempts"},{"next": "say_play_4","event": "providerError"},{"next": "say_play_4","event": "payInterrupted"},{"next": "say_play_4","event": "hangup"},{"next": "say_play_4","event": "validationError"}],"properties": {"security_code": true,"offset": {"x": -70,"y": 1140},"max_attempts": 2,"payment_connector": "","payment_amount": "{{widgets.function_1.body}}","description": "{{trigger.call.From}} via Twilio","currency": "usd","language": "en-US","postal_code": "false","payment_token_type": "reusable","timeout": 5,"valid_card_types": ["visa","master-card","amex","discover"]}},{"name": "say_play_3","type": "say-play","transitions": [{"next": "send_success_message_1","event": "audioComplete"}],"properties": {"voice": "man","offset": {"x": -185,"y": 1433},"loop": 1,"say": "Your donation of ${{widgets.function_1.body}} was successful, Thank you.","language": "en-US"}},{"name": "say_play_4","type": "say-play","transitions": [{"next": "send_fail_message_1","event": "audioComplete"}],"properties": {"voice": "man","offset": {"x": 190,"y": 1456},"loop": 1,"say": "There was an error with your donation. Please verify your details and call back to try again. Thanks!","language": "en-US"}},{"name": "send_success_message_1","type": "send-message","transitions": [{"event": "sent"},{"event": "failed"}],"properties": {"offset": {"x": -100,"y": 1700},"service": "{{trigger.message.InstanceSid}}","channel": "{{trigger.message.ChannelSid}}","from": "{{flow.channel.address}}","to": "{{contact.channel.address}}","body": "Your donation of ${{widgets.function_1.body}} was successful, Thank you!."}},{"name": "send_fail_message_1","type": "send-message","transitions": [{"event": "sent"},{"event": "failed"}],"properties": {"offset": {"x": 250,"y": 1700},"service": "{{trigger.message.InstanceSid}}","channel": "{{trigger.message.ChannelSid}}","from": "{{flow.channel.address}}","to": "{{contact.channel.address}}","body": "There was an error with your donation. Please verify your details and call back to try again. Thanks!"}},{"name": "confirm_donation_amount","type": "gather-input-on-call","transitions": [{"next": "split_2","event": "keypress"},{"event": "speech"},{"next": "confirm_donation_amount","event": "timeout"}],"properties": {"number_of_digits": 1,"speech_timeout": "auto","offset": {"x": 420,"y": 530},"loop": 1,"finish_on_key": "#","say": "You entered ${{widgets.function_1.body}}. Press 1 to confirm, or press 2 to enter a different amount.","stop_gather": true,"gather_language": "en","profanity_filter": "true","timeout": 5}},{"name": "split_2","type": "split-based-on","transitions": [{"next": "confirm_donation_amount","event": "noMatch"},{"next": "pay_1","event": "match","conditions": [{"friendly_name": "If value equal_to 1","arguments": ["{{widgets.confirm_donation_amount.Digits}}"],"type": "equal_to","value": "1"}]},{"next": "gather_1","event": "match","conditions": [{"friendly_name": "If value equal_to 2","arguments": ["{{widgets.confirm_donation_amount.Digits}}"],"type": "equal_to","value": "2"}]}],"properties": {"input": "{{widgets.confirm_donation_amount.Digits}}","offset": {"x": 430,"y": 800}}},{"name": "received_message_forward","type": "send-message","transitions": [{"event": "sent"},{"event": "failed"}],"properties": {"offset": {"x": -300,"y": 250},"service": "{{trigger.message.InstanceSid}}","channel": "{{trigger.message.ChannelSid}}","from": "{{flow.channel.address}}","to": "{{contact.channel.address}}","body": "Thanks for texting the Donation line. To donate, please call us on this same number. We do not accept donations via text."}}],"initial_state": "Trigger","flags": {"allow_concurrent_calls": true}}
当前处理函数
exports.handler = function(context, event, callback) { const amount = event.amount; const convert = amount.replace('*', '.'); callback(null, convert); };
解决方案
要把手机号关联到Stripe客户,核心是在创建支付时指定Stripe的customer参数。由于Twilio Studio的「捕获支付」组件默认不会自动关联客户,需要通过自定义函数处理Stripe客户的创建/关联,再完成支付。
步骤1:修改Twilio Flow,传递手机号到函数
在function_1的参数里新增手机号字段:
- 新增参数,
key填phoneNumber,value填{{trigger.call.From}}
修改后的function_1属性片段:
{"name": "function_1","type": "run-function","transitions": [{"next": "confirm_donation_amount","event": "success"},{"event": "fail"}],"properties": {"offset": {"x": -60,"y": 530},"parameters": [{"value": "{{widgets.gather_1.Digits}}","key": "amount"},{"value": "{{trigger.call.From}}","key": "phoneNumber"}],"url": "https://sand-pointer-5043.twil.io/pay_from_exchange"}}
步骤2:更新处理函数,对接Stripe API
在Twilio函数中引入Stripe SDK,实现以下逻辑:
- 处理金额格式并转换为Stripe要求的最小单位(分)
- 根据手机号查找现有Stripe客户,不存在则创建新客户
- 创建关联了客户ID的Stripe支付Intent,返回客户端Secret给Twilio
更新后的函数代码:
const stripe = require('stripe')(context.STRIPE_SECRET_KEY); exports.handler = async function(context, event, callback) { try { // 处理金额格式并转换为Stripe最小单位(分) const amount = event.amount; const convertedAmount = amount.replace('*', '.'); const stripeAmount = Math.round(parseFloat(convertedAmount) * 100); // 根据手机号查找或创建Stripe客户 const phoneNumber = event.phoneNumber; let customer; const existingCustomers = await stripe.customers.list({ phone: phoneNumber, limit: 1 }); if (existingCustomers.data.length > 0) { customer = existingCustomers.data[0]; } else { customer = await stripe.customers.create({ phone: phoneNumber, description: `Twilio电话支付客户:${phoneNumber}` }); } // 创建关联客户的支付Intent const paymentIntent = await stripe.paymentIntents.create({ amount: stripeAmount, currency: 'usd', customer: customer.id, description: `捐赠:$${convertedAmount} (来自${phoneNumber})` }); // 返回金额和支付Intent的客户端Secret callback(null, { amount: convertedAmount, clientSecret: paymentIntent.client_secret }); } catch (error) { callback(error); } };
步骤3:配置Twilio函数环境变量
在Twilio控制台的函数设置中,添加环境变量:
STRIPE_SECRET_KEY:你的Stripe秘钥(测试环境用测试秘钥,生产环境用生产秘钥)
步骤4:修改Twilio Studio的「捕获支付」组件
在pay_1组件中:
- 将
payment_amount改为{{widgets.function_1.body.amount}} - 在「高级选项」中找到「Payment Intent Client Secret」,填入
{{widgets.function_1.body.clientSecret}}
完成以上配置后,支付记录会自动关联到对应手机号的Stripe客户,你就能在Stripe后台通过手机号找到客户及其所有支付记录了。
内容的提问来源于stack exchange,提问作者Jay F
相关产品推荐
相关产品推荐

