In this tutorial, you will create an interactive voice response (IVR) that collects payment details from a customer and passes the information in a PCI-compliant manner to your payment processor of choice. This tutorial uses a TwiML Bin with the <Pay> verb and the Stripe Pay Connector to charge a credit card.
In this tutorial you will learn how to:
Enable PCI Mode on your account
Create a Stripe account
Configure a Stripe Pay Connector
Create a TwiML Bin with <Pay>
Create a Twilio Function that handles the payment result and provides new TwiML instructions
Buy a Twilio phone number
Configure the Twilio phone number to use the TwiML Bin for incoming calls
Test capturing credit card details and see the resulting charge on your Stripe account
In order to use any of Twilio's Pay Connectors, you must enable PCI mode on your Twilio account. This ensures that Twilio will capture the payment details from your customers in a PCI-compliant manner and redact any sensitive PCI information from any call logs.
PCI Mode will redact sensitive information from ALL of your account's logs. Turning on PCI Mode cannot be undone.
If you want to avoid redacting information from all logs on an account, consider creating another Twilio account, enable PCI Mode on that account, and use that account when collecting payments with <Pay>
or Agent Assist/Payment API.
To enable PCI Mode, complete the following steps:
Navigate to the Twilio Voice Settings in the Twilio Console. (In the left navigation pane, click on Voice > Settings > General.)
Click on the Enable PCI Mode button.
Click on I Agree on the pop up to accept Twilio's Terms of Service.
Click on the Save button.
<Pay>
allows you to use a variety of payment providers; we will use Stripe for this tutorial.
To get started, head over to Stripe to create your account.
In order to authorize Twilio to create charges and create tokens on your behalf, you must connect your Stripe account to Twilio's Stripe Connect platform. Complete the following steps:
In your Twilio Console, navigate to Pay Connectors under Voice > Manage > Pay Connectors in the left navigation pane and click on the Stripe Connector tile.
Click on Install.
On the pop-up, read the Terms of Service. Click the checkbox next to I agree to Twilio Inc's Terms of Service and click Agree & Install.
Enter Default for the UNIQUE NAME.
You can create one Pay Connector per Twilio account with the name Default. When <Pay>
is invoked, if the paymentConnector
attribute is not specified then the Default
Pay Connector is used.
Click on Connect with Stripe, which redirects you to Stripe. Enter your Stripe credentials and answer the prompts with your business details. If your Stripe account has not been activated to allow you to accept payments in live mode, then you can bypass entering your business details at this point and click the link at the top Skip this account form. You are then redirected back to Twilio.
You are directed back to the Stripe Connector page in the Twilio Console. Notice that the STRIPE ACCOUNT ID (acct_XXXXXXXXXXXXXXX
) is now shown on the page. If you have multiple Stripe accounts for Dev/Stage/Prod, the Account ID helps you differentiate these accounts for each Stripe Connector Instance
Next, we'll use a TwiML Bin to provide Twilio with the <Pay>
TwiML instruction. When Twilio executes the <Pay>
verb, a customer will hear prompts to enter their payment information.
Open a new browser tab and navigate to the TwiML Bins page in your Twilio Console.
Click on the blue + button to create a new TwiML Bin
Give the TwiML Bin a FRIENDLY NAME of "<Pay> with Stripe"
Copy and paste the following TwiML into the TWIML input box:
1<?xml version="1.0" encoding="UTF-8"?>2<Response>3<Say>Calling Twilio Pay</Say>4<Pay paymentConnector="Default" action="" chargeAmount="20.45"/>5</Response>
Notice how the <Pay>
verb has three attributes: paymentConnector
, action
, and chargeAmount
.
The paymentConnector
attribute's value is the Pay Connector you want to use with this <Pay>
verb. In this case, it's the Generic Pay Connector you just created called "Default".
The action
attribute is left blank for now. This attribute will be a URL. Upon the completion of a <Pay>
transaction, Twilio will send a webhook to your action
URL to get a new set of TwiML instructions. We'll create an endpoint in the next step that handles the request to this action
attribute.
The chargeAmount
attribute is set to 20.45
, representing a charge of $20.45. (If you wanted to create a tokenize transaction, you would set this attribute value to 0 or omit the attribute altogether.)
Click the Save button at the bottom of the page.
When <Pay>
completes capturing the consumers' credit card, the Pay Connector initiates a transaction with the Payment Provider. Next, the Twilio sends an HTTP request to <Pay>
's action
URL with a status
returned.
You will use Twilio's Serverless Functions to write and host the endpoint for our action
URL. Twilio Functions allow you deploy Node.js-based applications without needing to install anything locally on your own machine.
Navigate to the Functions > Services page in your Twilio Console.
Click on the Create Service button to create a new Functions service.
In the Name your Service pop up, give your service the name pay-with-stripe-action-url
Click Next on the Name your Service pop up.
In the Functions pane on the top left, rename the /path_1
Function to /pay
and hit Enter.
Next to the /pay path name, you should see a lock icon (When you hover over it, a "Protected" tooltip shows.). Click on the lock icon and click on Public.
Select all of the provided sample code in the code editor pane and delete it.
Copy and paste the following code into the code editor of your /pay function:
1exports.handler = function(context, event, callback) {2console.log("in Pay");3console.log(event);4console.log(event.Result);56let twiml = new Twilio.twiml.VoiceResponse();78switch (event.Result) {9case "success":10text = "Thank you for your payment";11break;12case "payment-connector-error":13text = "The Payment Gateway is reporting an error";14console.log(decodeURIComponent(event.PaymentError));15break;1617default:18text = "The payment was not completed successfully";19}20twiml.say(text);21callback(null, twiml);22};
Your Functions editor should now look like this: \
This Function checks the Result
property in the request from Twilio. If the Result
is "success"
, the Function returns the following TwiML:
1<?xml version="1.0" encoding="UTF-8"?>2<Response>3<Say>Thank you for your payment</Say>4</Response>
If the Result is "payment-connector-error"
, the function returns the following TwiML:
1<?xml version="1.0" encoding="UTF-8"?>2<Response>3<Say>The payment was not completed successfully</Say>4</Response>
Click on the blue Save button at the bottom of the code editor pane.
Click the Deploy All button at the bottom of the page. This deploys your Twilio Function to the internet.
Click on the Enable live logs switch so that you'll see live logs when your /pay endpoint receives a request from Twilio.
Now, copy the URL for your new endpoint by clicking on Copy URL (above the Live logs on toggle).
In a new browser tab, navigate to the <Pay> with Stripe TwiML Bin you created earlier.
Paste the Function URL from step 12 between the double quotation marks after action=
(i.e. in the place of https://example.com/pay
below).
1<?xml version="1.0" encoding="UTF-8"?>2<Response>3<Say>Calling Twilio Pay</Say>4<Pay paymentConnector="Default" chargeAmount="20.45"5action="https://example.com/pay"/>6</Response>
Click Save on the TwiML Bin page.
Now it's time to purchase Twilio phone number and configure it to use the <Pay> instructions in the TwiML Bin.
If you don't already own a Twilio Voice-enabled phone number, complete the following steps:
In the Twilio Console, navigate to the Buy a Number page.
Make sure the "Voice" checkbox is checked and click the Search button.
You'll see a list of available phone numbers and their capabilities. Find a number that you like and click the Buy button.
In the"Review Phone Number" pop-up, review the information and click on Buy (xxx) xxx-xxxx to confirm your purchase.
Now you'll navigate to the configuration page for the phone number and configure it to use your <Pay> with Stripe TwiML Bin when someone calls the number.
Before you call your Twilio phone number, review what should happen:
<Pay>
verb, so you hear prompts to enter your payment details.<Pay>
's action
attribute).<Say>Thank you for your payment.</Say>
TwiML back to Twilio.Call your Twilio number.
Enter your test payment information:
4242 4242 4242 4242
12 25
(pick a date in the future)94105
333
To verify that your application correctly captured this data, navigate to your Call Logs and click on your most recent call.
Scroll down to see the Request Inspector. Expand the parameters on the second POST
for your call.
You can navigate over to your Stripe Dashboard, copy the "PaymentConfirmationCode" "ch_xxxxx," and paste into the Stripe search. You should see the following:
You can add the line console.log(event);
in your Twilio Function's code. (Don't forget to save and deploy the changes.) This prints out the request parameters in the Log to let you inspect the contents.
You can also navigate to your Call Logs in the Console and view the call in more detail.
You will notice a POST
request to <Pay>
's action
URL for each stage of the call, first ringing
state, and then in-progress
.
If you expand the second POST
request parameters you will see the call status in-progress
. The Result field shows information on error causes. Read the <Pay> documentation for more information.
When Stripe rejects a transaction the result is payment-connector-error
and you can get further insight by inspecting the PaymentError field.
For instance, if instead of entering 4242 4242 4242 4242
you enter another card then you will see the following:
Stripe offers a list of test card numbers to help with error handling:
See Stripe's full list of test cards.
If you want a POST
request after each payment detail is collected, you can use <Pay>'s statusCallback attribute.
Great work! In a few lines of code, you've captured payment details and charged your customer in test mode.
For more information on <Pay>
, including unique attributes you can use to modify your payments, check out the <Pay> docs.