Checkout & payments
Apple Pay / Google Pay Setup
Indicative price, not an offer: €19/mo; credential: merchant ID setup, certificate management
Generated from docs/plan/paid/apple-pay-google-pay-setup/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Mobile shoppers convert 3x better with wallet payments. This paid credential module adds Apple Pay and Google Pay integration, handling merchant ID setup, certificate management, and hosted-mode authentication.
The problem
Mobile shoppers abandon when they see a form; Apple Pay and Google Pay let them pay with one tap. Without these options, I lose 30–50% of mobile conversions, but setting up merchant IDs and certificates is complex.
What it does
- Merchant ID configuration: store and validate Apple Pay merchant ID (merchant domain registration)
- Certificate management: guide merchant through CSR generation, Apple’s certificate issuance, and renewal tracking
- Google Pay token handling: accept encrypted payment token from Google, decrypt server-side, send to payment processor
- Apple Pay session: POST to Apple’s endpoint to create a session, validate domain ownership, return token
- Payment method detection: show Apple/Google Pay only if user’s device supports it (via client-side detection or feature flags)
- Hosted mode: both Apple and Google use hosted forms; AstroBaaS redirects to processor’s form and waits for webhook (no PCI burden)
- Fallback: if Apple Pay fails, show alternative payment methods (Stripe, PayPal, etc.)
- Token validation: verify Apple/Google Pay tokens are fresh (5-minute expiry) and match the order
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Certificate auto-renewal — merchant is responsible for renewing Apple’s merchant domain certificate before expiry. AstroBaaS shows a warning 30 days before expiry (stored in audit log or settings), but renewal clicks Apple’s website, not ours.
- Shipping address collection — Apple/Google Pay return an address; AstroBaaS passes it through to the storefront, but address normalization and validation (postal code format, region lookup) belong to the storefront.
- Device detection per-browser — module offers the methods; the storefront JS detects support via PaymentRequest API. AstroBaaS only needs to know which methods are configured.
Data model
No new collections. On Setting (or PluginSettings if using plugin config): store apple_pay_merchant_id (string), apple_pay_merchant_domain (string), google_pay_merchant_id (string). Store certificate expiry dates and renewal tracking in audit log or a dedicated config file (not the schemaless settings table to avoid accidental exposure).
API
- GET /api/payments/methods — returns array of enabled methods including ‘applepay’, ‘googlepay’
- POST /api/payments/apple-pay/session — { validation_url } → validate domain, return session token
- POST /api/payments/google-pay/token — { encrypted_token } → decrypt and forward to processor webhook
- GET /api/payments/configuration — returns { apple_pay_enabled, google_pay_enabled, merchant_ids_configured }
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Payments panel has sections for Apple Pay and Google Pay. Apple Pay section shows: ‘Merchant Domain: merchant.example.com’, ‘Certificate Expires: 2025-09-15’, ‘Renew Certificate’ button (links to Apple Developer Portal). Google Pay section shows merchant ID and test-mode toggle. Expired certificate shows a red warning.
The seam — why this is paid
Core owns the Session model and webhook routing. Paid module owns Apple/Google specific session creation, token validation, and certificate lifecycle tracking. The split: core’s POST /api/payments/start is generic; Apple Pay and Google Pay are two implementations. Wallet payment tokens are forwarded to the underlying processor (Stripe, Adyen, etc.), so this module plays a small role in the larger payment flow.
Credential: Apple/Google merchant ID management, certificate handling, PCI
Dependencies
- payment-status-tracking — relies on payment_status state machine
- adyen-payment-gateway or equivalent processor integration — wallet tokens need somewhere to go
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- Merchant configures merchant ID ‘merchant.example.com’; /api/payments/configuration returns apple_pay_enabled=true
- Storefront calls /api/payments/apple-pay/session with validation URL; response includes session token
- Google Pay button clicked; encrypted token sent to /api/payments/google-pay/token; token decrypted, amount/currency validated, forwarded to Stripe (or other processor)
- Certificate expiry 30 days away; admin dashboard shows red warning ‘Apple Pay certificate expires Sep 15’
- Storefront requests methods via /api/payments/methods; Apple Pay is included; Google Pay is not (merchant never configured it)
- User on Android device sees Google Pay option; user on old iPhone (pre-iOS 15) does not see Apple Pay (client-side capability check)
- Token arrives with wrong currency or amount → rejected, order stays ‘unpaid’, audit-logged as suspicious
- Same token replayed (same token id) → no-op, order stays in current state (idempotent)
Risks
Certificate expiry can block Apple Pay silently — if certificate expires and AstroBaaS does not validate it on startup, Apple Pay sessions fail at runtime. Validation must happen at boot and every hour (via cron). Token decryption must never log the plaintext token; logging encrypted tokens only. Merchant ID validation: if a config mismatch exists (certificate for ‘merchant.example.com’, but code checks ‘api.example.com’), Apple Pay sessions silently fail — domain must be validated at config-load time, not at checkout.
Commercial context
| Suggested price | €19/mo; credential: merchant ID setup, certificate management |
| Rival anchor | Shopify: free (native); Magento: free + extensions; custom: €1200+ |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.