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:

  1. Generic card
  2. List card

🚧

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.

477

What the Messenger checkout experience looks like.

Getting started

On Facebook (necessary even in development mode):

  1. Setup your payment provider
  2. Re-generate a page access token
  3. Subscribe to Payment webhook events (read more about the webhook events - ONLY subscribe to the webhook events you want to support)

On Meya:

  1. Save your Messenger integration with the new page access token
  2. Add a flow with a buy button
1071

Make sure you connect your Facebook page to Stripe, Paypal or other payment processor.

📘

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.

2132

📘

Works with

FIXED_AMOUNT and FLEXIBLE_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.

2134

📘

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.

2148

📘

Works with

FIXED_AMOUNT and FLEXIBLE_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 nameDesription
currencyCurrency for priceRequired
is_test_paymentWhether this is a test payment. Once set to true, the charge will be a dummy charge.
payment_typeThe type of payment you want Messenger to process.Required
FIXED_AMOUNT or FLEXIBLE_AMOUNT
merchant_nameName of merchantRequired
requested_user_infoInformation requested from person that will render in the dialog. You can config these based on your product needRequired
shipping_address, contact_name, contact_phone, contact_email
price_listList of objects used to calculate total price. Each label is rendered as a line item in the checkout dialogRequired
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

NameDescriptionType
idID of shipping addressString
countryShipping address countryString
cityShipping address cityString
street_1Shipping address street, first lineString
street_2Shipping address street, second lineString
stateShipping address stateString
postal_codeShipping address postal codeString
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 nameDescriptionType
option_idID of shipping optionString
option_titleTitle of optionString
price_listList of line items that are used to update pricing.Array

price_list object

Field nameDescriptionType
labelLabel of line itemString
amountAmount of line itemString

📘

Important

Note the option_id. It will be made available in the flow scope under shipping_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 nameDescriptionValues
successWhether 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 nameDescriptionType
provider_typePayment provider type (one of stripe/paypal/token)String
charge_idPayment provider charge id (for stripe/paypal, null for tokenization), for a test_payment, the charge id will be test_charge_id_12345String
tokenized_cardPGP-signed tokenized charge card (null for stripe/paypal)String
tokenized_cvvPGP-signed CVV number (null for stripe/paypal)String
token_expiry_monthExpiry month (null for stripe/paypal)String
token_expiry_yearExpiry year (null for stripe/paypal)String
fb_payment_idA 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 nameDescriptionType
shipping_addressPerson's shipping addressObject
contact_namePerson's nameString
contact_emailPerson's emailString
contact_phonePerson's phone numberString

shipping_address object

NameDescriptionType
idID of shipping addressString
countryShipping address countryString
cityShipping address cityString
street_1Shipping address street, first lineString
street_2Shipping address street, second lineString
stateShipping address stateString
postal_codeShipping address postal codeString

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 NameDescriptionType
currencyCurrency of amountString
amountTotal amountString

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 }}