Payments
How to accept payments via Facebook Messenger
Your bot can accept payments from your users through Messenger using a Buy button.
Buy buttons can be used on 2 card types:
Payments are currently in beta. The feature can be tested by any bot in development mode, but the bot must be accepted into Facebook's beta program to receive payments in production. Request access here.
Getting started
On Facebook (necessary even in development mode):
- Setup your payment provider
- Re-generate a page access token
- Subscribe to Payment webhook events (read more about the webhook events - ONLY subscribe to the webhook events you want to support)
On Meya:
- Save your Messenger integration with the new page access token
- Add a flow with a buy button
Example Payments bot
You can clone our example payments bot to get you started. Note: make sure you've subscribed to all the proper webhook events.
Example payments bot - https://github.com/meya-ai/messenger-payments
Payment webhook events
There are 3 payment webhooks you can subscribe to: messaging_payments
, messaging_checkout_updates
and messaging_pre_checkouts
.
Subscribing to messaging_payments
with a payment_type of FIXED_AMOUNT
is the simplest use-case.
Messaging Payment Event
You must be subscribed to the event messaging_payments
to use payments.
Works with
FIXED_AMOUNT
andFLEXIBLE_AMOUNT
payment types
Checkout Updates Event
You must subscribe to messaging_checkout_updates
. This enables you to update pricing with shipping and taxes based on a person's location. This callback is made each time the shipping address is changed. Only use if you plan on changing the price of shipping and/or taxes depending on the shipping address.
Works with
FLEXIBLE_AMOUNT
payment type only.
Pre Checkout Event
You must subscribe to messaging_pre_checkouts
. This allows you to do any processing on your end before charging user's card. You could check inventory levels or for price changes before accepting the payment.
Works with
FIXED_AMOUNT
andFLEXIBLE_AMOUNT
payment types
Buy button
The buy button is the way to initiate the payment process on Messenger. It can be added to your flow definition on a generic card or list card. The buy button spec is different from other buttons and has a required object called payment_summary
.
payment_summary
object
Field name | Desription | |
---|---|---|
currency | Currency for price | Required |
is_test_payment | Whether this is a test payment. Once set to true, the charge will be a dummy charge. | |
payment_type | The type of payment you want Messenger to process. | RequiredFIXED_AMOUNT or FLEXIBLE_AMOUNT |
merchant_name | Name of merchant | Required |
requested_user_info | Information requested from person that will render in the dialog. You can config these based on your product need | Requiredshipping_address , contact_name , contact_phone , contact_email |
price_list | List of objects used to calculate total price. Each label is rendered as a line item in the checkout dialog | Required |
buttons:
- text: Buy
type: buy
payment_summary:
currency: USD
is_test_payment: true
payment_type: FIXED_AMOUNT
merchant_name: "The bike store"
requested_user_info:
- contact_email
- shipping_address
price_list:
- label: "Rockin' Monster Bike"
amount: 39.97
name: buy_button
states:
card_state:
component: meya.card
properties:
title: "Rockin' Monster Bike"
text: Awesome bike for awesome kids. Goes fast.
image_url: "https://i5.walmartimages.com/asr/9f840e81-43d8-425c-be41-f8aceb218d52_1.f82698857efde929bda61cca0fb98a4a.jpeg"
output: button_click
buttons:
- text: Buy
type: buy
payment_summary:
currency: USD
is_test_payment: true
payment_type: FIXED_AMOUNT
merchant_name: "The bike store"
requested_user_info:
- contact_email
- shipping_address
price_list:
- label: "Rockin' Monster Bike"
amount: 39.97
Fixed vs Flexible payment types
Facebook allows 2 types of payment types - fixed and flexible. Flexible allows you to change the total depending on the shipping options selected by the user and their shipping address. You must subscribe to messaging_checkout_updates
to use flexible.
is_test_payment
is set to true during testing. Don't forget to remove this property once you are in production.
Fixed example
For FIXED_AMOUNT
payment types the total is calculated from your price_list
amounts.
states:
card_state:
component: meya.card
properties:
title: "Rockin' Monster Bike"
text: Awesome bike for awesome kids. Goes fast.
image_url: "https://i5.walmartimages.com/asr/9f840e81-43d8-425c-be41-f8aceb218d52_1.f82698857efde929bda61cca0fb98a4a.jpeg"
output: button_click
buttons:
- text: Buy
type: buy
payment_summary:
currency: USD
is_test_payment: true
payment_type: FIXED_AMOUNT
merchant_name: "The bike store"
requested_user_info:
- contact_email
- shipping_address
price_list:
- label: "Rockin' Monster Bike"
amount: 39.97
Flexible payment type example
states:
card_state:
component: meya.card
properties:
title: "Rockin' Monster Bike"
text: Awesome bike for awesome kids. Goes fast.
image_url: "https://i5.walmartimages.com/asr/9f840e81-43d8-425c-be41-f8aceb218d52_1.f82698857efde929bda61cca0fb98a4a.jpeg"
output: button_click
buttons:
- text: Buy
type: buy
callbacks:
checkout_update: shipping_options
payment_summary:
currency: USD
is_test_payment: true
payment_type: FLEXIBLE_AMOUNT
merchant_name: "The bike store"
requested_user_info:
- contact_name
- shipping_address
price_list:
- label: "Rockin' Monster Bike"
amount: 39.97
Note the callbacks section in the flow. The checkout_update
value is required and should reference a component you've created (see the shipping_prices
component example below). The component you create in this context will have 1 property available called shipping_address
.
shipping_address
object available to your checkout_update
callback component
Name | Description | Type |
---|---|---|
id | ID of shipping address | String |
country | Shipping address country | String |
city | Shipping address city | String |
street_1 | Shipping address street, first line | String |
street_2 | Shipping address street, second line | String |
state | Shipping address state | String |
postal_code | Shipping address postal code | String |
from meya import Component
class ShippingPrices(Component):
def start(self):
address = self.properties.get("shipping_address", {})
fedex_amount = 16.99
state = address.get("state", "")
if state == "CA":
fedex_amount = 4.99
data = {
"shipping": [
{
"option_id": "1",
"option_title": "Fedex",
"price_list": [
{
"label": "Shipping",
"amount": fedex_amount
},
{
"label": "Tax",
"amount": 1.99
}
]
},
{
"option_id": "2",
"option_title": "USPS",
"price_list": [
{
"label": "Shipping",
"amount": 2.99
},
{
"label": "Tax",
"amount": 1.44
}
]
}
]
}
return self.respond(data=data)
You must set your data response to a list of shipping
objects.
shipping
object.
Field name | Description | Type |
---|---|---|
option_id | ID of shipping option | String |
option_title | Title of option | String |
price_list | List of line items that are used to update pricing. | Array |
price_list
object
Field name | Description | Type |
---|---|---|
label | Label of line item | String |
amount | Amount of line item | String |
Important
Note the
option_id
. It will be made available in theflow
scope undershipping_option_id
once the payment has been processed. See reference here.
Pre-checkout example
This callback will occur when a user clicks on Pay in the payment dialog, but before the user's card is charged.
This allows you to do any processing on your end before charging user's card. You could check inventory levels or for price changes before accepting the payment.
If your app does not subscribe to this event, after the user clicks on Pay Facebook will process the payment directly.
states:
card_state:
component: meya.card
properties:
title: "Rockin' Monster Bike"
text: Awesome bike for awesome kids. Goes fast.
image_url: "https://i5.walmartimages.com/asr/9f840e81-43d8-425c-be41-f8aceb218d52_1.f82698857efde929bda61cca0fb98a4a.jpeg"
output: button_click
buttons:
- text: Buy
type: buy
callbacks:
pre_checkout: stock_check
payment_summary:
currency: USD
is_test_payment: true
payment_type: FIXED_AMOUNT
merchant_name: "The bike store"
requested_user_info:
- contact_email
- shipping_address
price_list:
- label: "Rockin' Monster Bike"
amount: 39.97
Note the callbacks section in the flow. The pre_checkout
value is required if you subscribe to messaging_pre_checkouts
and should reference a component you've created (see the stock_check
component example below). The component you create in this context will have 2 properties available called requested_user_info
and amount
.
from meya import Component
class StockCheck(Component):
def start(self):
# Don't let the payment go through if the value is > 200
amount = float(self.properties.get("amount", {}).get("amount", 0))
if amount > 200:
success = False
else:
success = True
data = {"success": success}
return self.respond(data=data)
The JSON data response you set must be in the format below.
Field name | Description | Values |
---|---|---|
success | Whether the pre-checkout processing is successful. | True or False |
Post-payment Processing
Once the payment has been processed successfully by Facebook, your flow will advance to the next state. The following fields are available on flow
scope.
payment_credential
, requested_user_info
, amount
, and shipping_option_id
. The fields for each object are listed below.
Payment credential
The payment_credential
object contains data you should save to your records such as the charge_id and the fb_payment_id.
payment_credential:
component: meya.text
properties:
text: Payment credentials - {{ flow.payment_credential }}
self.db.flow.get("payment_credential")
Field name | Description | Type |
---|---|---|
provider_type | Payment provider type (one of stripe/paypal/token) | String |
charge_id | Payment provider charge id (for stripe/paypal, null for tokenization), for a test_payment, the charge id will be test_charge_id_12345 | String |
tokenized_card | PGP-signed tokenized charge card (null for stripe/paypal) | String |
tokenized_cvv | PGP-signed CVV number (null for stripe/paypal) | String |
token_expiry_month | Expiry month (null for stripe/paypal) | String |
token_expiry_year | Expiry year (null for stripe/paypal) | String |
fb_payment_id | A facebook issued payment id for tracking. If it is a test payment, the id will be test_payment_id_12345. | String |
Shipping Option ID
The shipping_option_id
is only relevant if you've used the FLEXIBLE payment type. This was the shipping option selected by the user and references one of the options you provided earlier.
shipping_option:
component: meya.text
properties:
text: Shipping option id - {{ flow.shipping_option_id }}
self.db.flow.get("shipping_option_id")
Requested User Info
The requested_user_info
object references any fields you requested earlier in your buy button.
requested_user_info:
component: meya.text
properties:
text: Requested user info - {{ flow.requested_user_info }}
self.db.flow.get("requested_user_info")
Field name | Description | Type |
---|---|---|
shipping_address | Person's shipping address | Object |
contact_name | Person's name | String |
contact_email | Person's email | String |
contact_phone | Person's phone number | String |
shipping_address
object
Name | Description | Type |
---|---|---|
id | ID of shipping address | String |
country | Shipping address country | String |
city | Shipping address city | String |
street_1 | Shipping address street, first line | String |
street_2 | Shipping address street, second line | String |
state | Shipping address state | String |
postal_code | Shipping address postal code | String |
Amount
The amount
object available post-payment processing.
post_pay_amount:
component: meya.text
properties:
text: Amount - {{ flow.amount }}
self.db.flow.get("amount")
Field Name | Description | Type |
---|---|---|
currency | Currency of amount | String |
amount | Total amount | String |
Example flow with buy button and next state
states:
card_state:
component: meya.card
properties:
title: Huffy Rock It 12" bike
text: The lime green Huffy Rock It bike shines as they rides down the sidewalk.
image_url: "https://i5.walmartimages.com/asr/9f840e81-43d8-425c-be41-f8aceb218d52_1.f82698857efde929bda61cca0fb98a4a.jpeg"
output: button_click
buttons:
- text: Buy
type: buy
callbacks:
checkout_update: shipping_prices
payment_summary:
currency: USD
is_test_payment: true
payment_type: FLEXIBLE_AMOUNT
merchant_name: "The bike store"
requested_user_info:
- contact_email
- shipping_address
price_list:
- label: 'Huffy 12" bike'
amount: 39.97
- label: 'taxes'
amount: 11.03
on_success:
component: meya.text
properties:
text: >
Your charge id is {{ flow.payment_credential.charge_id }} and
your requested info is {{ flow.requested_user_info.contact_email }}
Updated over 6 years ago