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 a Generic Pay Connector.
In this tutorial, you will learn how to:
Generic Pay Connectors only handle sending payment details to a payment processor. It is up to the payment processor of your choosing to process a transaction and return a response to Twilio in the required format.
In this tutorial, we will create a mock payment processor that is not PCI compliant and not production-ready.
Depending on your real-life use case, you may choose to create your own PCI-compliant payment processor application or use another, existing payment processor. If you choose an existing payment processor, they will need to write custom code to interface with Twilio's Generic Pay Connectors.
Twilio also offers branded Pay Connectors that are already designed to integrate with the following payment processors/gateways: Stripe, CardConnect, Base Commerce, Chase Paymentech, Braintree, and Adyen.
Before you continue with this tutorial, you should familiarize yourself with the <Pay> and Generic Pay Connector docs.
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:
Click on the Enable PCI Mode button.
In order for Twilio to send payment information to your payment processor, you must install the Generic Pay Connector on your Twilio Account.
Click on the Generic Pay Connector tile.
For now, set the ENDPOINT URL to https://example.com. This is the endpoint where your payment processor will receive HTTPS POST
requests from Twilio. We will create a Twilio Function endpoint to handle these POST
requests later in this tutorial.
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.
Copy and paste the following TwiML into the TWIML input box:
_10<?xml version="1.0" encoding="UTF-8"?>_10<Response>_10 <Pay paymentConnector="My_Pay_Connector" action="" chargeAmount="10.00" />_10</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 "My_Pay_Connector".
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 another TwiML Bin with a new set of TwiML instructions for Twilio to execute after a completed <Pay> transaction and put that new TwiML Bin's URL in this action
attribute.
The chargeAmount
attribute is set to 10.00, representing a charge of $10.00. (If you wanted to create a tokenize transaction, you would set this attribute value to 0 or omit the attribute altogether.)
In the TWIML input box, paste the follwing TwiML:
_10<?xml version="1.0" encoding="UTF-8"?>_10<Response>_10 <Say>Thank you for entering your payment information.</Say>_10 {{#PaymentError}}_10 <Say>Sorry. There was an error: {{PaymentError}}</Say>_10 {{/PaymentError}}_10 <Hangup />_10</Response>
When Twilio executes this TwiML Bin, a caller will hear "Thank you for entering your payment information." and the call will end.
If Twilio's webhook request to this TwiML Bin contains a PaymentError property (meaning that an error occurred), the caller will also hear "Sorry. There was an error." along with a description of the error before the call ends. (This TwiML Bin uses a conditional content template. Read more about TwiML Templates on the "How to use templates with TwiML Bins" support page.)
action
attribute for the other TwiML Bin you created, so that Twilio will execute this set of TwiML instructions after the <Pay> verb has completed in your other TwiML Bin.
Paste the copied URL between the action
attribute's quotation marks. Your TwiML Bin should look something like this:
_10<?xml version="1.0" encoding="UTF-8"?>_10<Response>_10 <Pay paymentConnector="My_Pay_Connector" action="https://handler.twilio.com/twiml/EHXXXXXXXXXXXXXXXXXXXXXXXXX" chargeAmount="10.00" />_10</Response>
If you don't already own a Twilio Voice-enabled phone number, complete the following steps:
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.
Now you'll navigate to the configuration page for the phone number and configure it to use your Generic Pay Connector Tutorial <Pay> TwiML Bin when someone calls the number.
At this point, you can call your Twilio phone number from another phone. You should hear "Please enter your credit card number."
You can try entering the following test card details:
4111 1111 1111 1111
12 25
(or any date in the future)
94105
333
You should hear: "Thank you for entering your payment information. Sorry. There was an error: Payment Gateway rejected charge creation. Got malformed response from Payment Provider."
This makes sense, given that our Generic Pay Connector's Endpoint URL was configured to be "https://example.com". That's not a real payment processor.
In the next section, we'll create a very basic endpoint that will be able to handle the POST
request that Twilio would send to a payment processor. This is just to illustrate the flow of information between your caller, Twilio, and the payment processor's endpoint.
When <Pay> has completed gathering valid payment information from the caller, the Generic Pay Connector will initiate an HTTPS POST
request to the endpoint URL specified in the Pay Connector's configuration.
In an actual, production situation, this is the step where a PCI-compliant payment processor would send the payment information and transaction details (i.e. whether the transaction is a tokenize or charge transaction) with the payment gateway, handle any timeouts or failures from the payment gateway, process any errors, and send back a response to Twilio in Twilio's required format.
For this tutorial, we will create an endpoint that will:
POST
request from Twilio with the transaction information and payment details
You will use Twilio's Serverless Functionsto write and host this code. Twilio Functions allow you deploy Node.js-based applications without needing to install anything locally on your own machine.
Click on the Add + button in the top left corner of the page and then click on Add Function.
In the Functions pane on the top left, find the input that contains /path_1 and change it to /pay and hit Enter.
Next to the /pay path name, you should see Protected. Click on the downward arrow next to Protected and then Public.
Copy and paste the following code into the code editor of your /pay function:
_15exports.handler = (context, event, callback) => {_15 console.log('POST request from Twilio received:');_15_15 console.log(event);_15_15 console.log('Authorization Headers: ', event.request.headers.authorization);_15_15 const sampleResponse = {_15 charge_id: "some_id",_15 error_code: null,_15 error_message: null_15 };_15_15 return callback(null, sampleResponse);_15};
Click on the Enable live logs switch so that you'll see live logs when your /pay endpoint receives a request from Twilio.
Paste your Twilio Function's URL into the ENDPOINT URL field.
Before you call your Twilio phone number, review what should happen:
paymentConnector
attribute pointed to your Generic Pay Connector called "My_Pay_Connector", which is configured with your Twilio Functions URL).
POST
request to your "Generic Pay Tutorial Action" TwiML Bin. (Remember, the <Pay> verb in the other TwiML Bin had an
action
attribute that pointed to this TwiML Bin.)
Enter your test payment information:
4111 1111 1111 1111
12 25
(or any date in the future)
94105
333
In your Twilio Functions log, you should see the logs of your Twilio Function print out something like the following:
_10_10POST request from Twilio received:_10_10{ request: '[Object]', transaction_id: '72500aba-328c-445c-a12e-da5700e10b17', _10cardnumber: '4111111111111111', cvv: '333', expiry_month: '12', _10expiry_year: '25', description: '[Object]', amount: '10.00', _10currency_code: 'USD', postal_code: '94150', method: 'charge', _10parameters: '[Object]' }_10_10Authorization Headers: Basic bXlfY29ubmVjdG9yX3VzZXJuYW1lOnRlc3RpbmcxMjM=
Notice that the card information is not redacted, since it is presumed that the payment processor is PCI-compliant.
Also, you can see that the request that Twilio sends to the payment processor contains an Authorization
header that conforms to HTTP Basic Authentication. It contains the credentials you used in your Generic Pay Connector's configuration in Base64-encoded form. (The above example decoded is: my_connector_username:testing123
.)
Your mock-payment-processor Function also returns an object in the format that Twilio expects:
_10{_10 charge_id: "some_id",_10 error_code: null,_10 error_message: null_10}
To see what Twilio sends to your <Pay> verb's action
URL, you can go to your Call Logs in your Twilio Console.
POST
requests.
POST
request to your Generic Pay Connector Tutorial <Pay> TwiML Bin. (Look at the
Response
Body
to verify this.)
Twilio made a second POST
request to the action
URL from your <Pay> verb. (Remember that your action
URL pointed to your Generic Pay Connector Tutorial Action TwiML Bin.) Inspect the Parameters in this POST
Request and see the parameters related to this <Pay> transaction:
PaymentCardNumber
PaymentConfirmationCode
Result
SecurityCode
PaymentCardType
ExpirationDate
PaymentCardPostalCode
PaymentToken
PaymentError
Congratulations! You completed a mock payment with <Pay> and a Generic Pay Connector. You should now understand the flow of information between a caller (your customer), Twilio, and your payment processor.
In order to become production-ready, you'll need to create your own PCI-compliant payment processor or find an existing, PCI-compliant payment processor that can properly respond to Twilio requests, process charge and tokenize transactions with a payment gateway, and handle timeouts and retries.
If you've already chosen a payment processor to work with, you can share the Generic Pay Connector docs with them so they can customize their code to work with Twilio's Generic Pay Connectors.