Apple Pay (AMB)

Send an Apple Pay request inside an Apple Messages for Business conversation. Blooio manages the mTLS merchant session for you and relays payment.received / payment.completed webhooks.

Apple Pay lets a customer complete a purchase inside an Apple Messages for Business (AMB) conversation — they tap a Pay bubble, authorize with Face ID / Touch ID, and the payment is confirmed in-thread. This page covers the full flow: one-time setup, sending a request, and the webhooks you receive.

Apple Pay is an AMB-only message type (channel_type: "amb"), part of the v4 API (api.blooio.com/v4; staging api-staging.blooio.com/v4).

How it works

Apple Pay in Messages for Business requires a merchant session — a short-lived, signed handshake with Apple over mutual TLS (mTLS). Blooio manages that handshake for you:

  1. You send an interactive apple_pay message with a paymentRequest.
  2. Blooio opens the merchant session with Apple over mTLS using your Merchant Identity Certificate, and injects the signed session into the outbound payload before it reaches Apple.
  3. Apple renders the payment sheet to the customer. They authorize on-device.
  4. Apple calls Blooio's hosted callbacks server-to-server, and Blooio relays them to your webhook as payment.received and payment.completed.

Note Blooio never sees the customer's card. The Apple Pay token in payment.received is encrypted with your Payment Processing Certificate — only you (or your payment processor) can decrypt and settle it. Blooio only holds the Merchant Identity Certificate, which authenticates the merchant session; it cannot decrypt payments.

The end-to-end payment lifecycle, from your send to payment.received and payment.completed. Drag to pan; use the controls to zoom or fit.

One-time setup

Set Apple Pay up once per channel in the dashboard under Integrations → Apple Messages for Business → Set up Apple Pay. The wizard walks through five steps. All of the following must be in place before a send will succeed — a missing piece surfaces as a 417 Payment Services Exception from Apple (see Troubleshooting).

Step What it is Notes
Merchant details Your Apple Pay Merchant ID (e.g. merchant.yourbrand) and display name Create it in the Apple Developer portal (Identifiers → Merchant IDs).
Verify domain The domain that owns the Apple Pay sessions Blooio hosts the domain-association file for you on its API domain — you do not host anything. Just register the domain on your Merchant ID in Apple Developer.
Identity cert The Apple Pay Merchant Identity Certificate Upload an existing PEM/.p12, or generate a CSR in the wizard, create the cert in Apple Developer, and upload the resulting .cer. Blooio stores it encrypted, per-brand.
Processing cert The Apple Pay Payment Processing Certificate Required by Apple for merchant validation, even though Blooio never uses it. Create it on your Merchant ID in Apple Developer and keep the private key. You then confirm you've created it — this gates the channel to ready.
Review Confirms all of the above are green The channel only becomes ready once the identity cert, verified domain, and processing-cert acknowledgement are all present.

Note Also link the Merchant ID to your Messages for Business account in Apple Business Register → your MfB account → Apple Pay → Merchant ID. Registering the cert in Apple Developer is not enough on its own; if the Merchant ID isn't linked to the MfB account, Apple returns merchantId ... not registered for service (417).

One-time setup: from creating your Merchant ID to a send-ready channel.

Sending an Apple Pay request

Send an interactive message with kind: "apple_pay". You only provide the paymentRequest (line items, total, currency). Blooio injects the merchantIdentifier, the signed merchant session, and the hosted callback endpoints for you.

curl -X POST 'https://api.blooio.com/v4/channels/ch_YOUR_CHANNEL/messages' \
  -H 'Authorization: Bearer bl_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
    "to": "urn:mbid:AQAA...",
    "interactive": {
      "kind": "apple_pay",
      "received_message": { "title": "Complete your payment" },
      "reply_message": { "title": "Payment" },
      "payment": {
        "paymentRequest": {
          "applePay": {
            "merchantCapabilities": ["supports3DS"],
            "supportedNetworks": ["visa", "masterCard", "amex"]
          },
          "lineItems": [{ "label": "Subtotal", "amount": "9.00" }],
          "total": { "label": "Your Brand", "amount": "9.00", "type": "final" },
          "currencyCode": "USD",
          "countryCode": "US"
        }
      }
    }
  }'
Try it

The response is a normal v4 message object with type: "interactive". Its id (minus the msg_ prefix) becomes the requestIdentifier that ties the later payment webhooks back to this request.

Note If the channel isn't fully set up, the send fails fast with error.code = "apple_pay_not_configured" and a message describing what's missing — the message is never handed to Apple.

Payment lifecycle & webhooks

This is the part that surprises people: a single successful payment produces two webhooks, because Apple drives two separate server-to-server callbacks.

Event Fired when Carries
payment.received The customer authorizes and Apple posts the encrypted token to Blooio's hosted paymentGateway callback The Apple Pay token — decrypt with your Payment Processing cert and settle
payment.completed Apple posts to Blooio's hosted orderTracking callback after the order is finalized Order / tracking details; no token
payment.failed The payment is declined, cancelled, or errors Failure context

So seeing both payment.received and payment.completed for one purchase is expected and correct:

  • payment.received = "the customer paid — here's the token, go settle it."
  • payment.completed = "the order is finalized."

Note The in-conversation Paid bubble that also appears is a visual confirmation in the thread. It is rendered as a payment card in the dashboard but does not emit its own webhook — that would just duplicate payment.received. The authoritative payment webhooks always come from the two callbacks above.

payment.received payload

{
  "type": "payment.received",
  "data": {
    "chat_id": "chat_...",
    "channel_id": "ch_...",
    "channel_type": "amb",
    "message_id": "msg_...",
    "payment": {
      "request_id": "019fb2...",
      "status": "received",
      "token": { "paymentData": { "...": "encrypted Apple Pay token" } },
      "billing_contact": { "...": "..." },
      "shipping_contact": { "...": "..." },
      "shipping_method": null,
      "order": null
    }
  }
}

payment.completed payload

{
  "type": "payment.completed",
  "data": {
    "chat_id": "chat_...",
    "channel_id": "ch_...",
    "channel_type": "amb",
    "message_id": "msg_...",
    "payment": {
      "request_id": "019fb2...",
      "status": "completed",
      "token": null,
      "order": { "...": "order / tracking details" }
    }
  }
}
Field Description
payment.request_id Matches the outbound message id (without the msg_ prefix). Use it to correlate both events with the original request.
payment.status received or completed.
payment.token Encrypted Apple Pay token. Only present on payment.received. Decrypt with your Payment Processing certificate to settle.
message_id The originating request message, so the events thread onto the conversation.

Note Idempotency: treat webhooks as at-least-once. Key your settlement logic on payment.request_id so a redelivered payment.received doesn't double-charge or double-settle.

All webhooks are HMAC-signed — verify them with the X-Blooio-Signature header as described in Verify webhook signatures.

Settling the payment

  1. On payment.received, take data.payment.token.
  2. Decrypt it with your Payment Processing Certificate private key (or hand it to your payment processor / gateway, which does this for you).
  3. Capture / settle the funds through your processor.
  4. Reconcile against payment.completed for order finalization.

Blooio is not in the money path — it brokers the Apple merchant session and relays events. Funds settle directly through your processor.

Live checkout updates (optional)

If your checkout needs to react while the customer is still on the payment sheet — recalculating tax when they pick a shipping address, or adjusting totals when they switch shipping method or card — you can register update endpoints (shippingContactUpdateUrl, shippingMethodUpdateUrl, paymentMethodUpdateUrl). Apple calls them before authorization and expects a response within 30 seconds. Blooio's default hosted flow doesn't advertise these, so you only need them for custom, dynamic checkouts.

Optional mid-checkout updates. These fire only when you register the update endpoints.

Troubleshooting

417 Payment Services Exception: merchantId=... not registered for service

Apple rejected the merchant session. Work through these in order — any one can cause it:

  1. Merchant ID not linked to Messages for Business. In Apple Business Register, open your MfB account → Apple Pay and set the Merchant ID. This is the most common cause.
  2. Missing Payment Processing Certificate. Apple requires this cert to exist on the Merchant ID for validation, even though Blooio never uses it. Create it in Apple Developer.
  3. Wrong gateway. Merchant validation is always minted against Apple's production gateway (apple-pay-gateway.apple.com), even for Internal Test Accounts paying with sandbox tester Apple IDs. The -cert host is Apple's internal MSP-certification environment and returns "not registered" for normal merchants.
  4. Domain not verified. The domain must be registered on the Merchant ID in Apple Developer. Blooio hosts the association file — confirm the Verify domain step is green in the wizard.

The send fails immediately with apple_pay_not_configured

The channel isn't fully set up. Re-open the wizard and make sure the identity cert, verified domain, and processing-cert acknowledgement are all green.

I only see the Paid bubble but no webhook

The bubble is cosmetic. Confirm your webhook endpoint is reachable (a 404 in the webhook delivery log means your server rejected it), then check the delivery log for payment.received / payment.completed.