Skip to main content

How to Test Apple Pay, Google Pay, and PayPal Flows

Short answer

Wallet checkout converts on mobile but cannot run fully in Linux headless CI—Apple Pay needs Safari on Apple hardware; Google Pay availability varies; PayPal uses popups or redirects. Split coverage: probe Assert for order creation on every PR, wallet UI on macOS/browser matrix or manual/nightly jobs, and explicit fallback-to-card scenarios when the wallet sheet is dismissed.

Part of Testing Guides by business flow.

Who this is for

Ecommerce and SaaS teams accepting Apple Pay, Google Pay, Link, or PayPal alongside cards—often via Stripe Payment Request Button, Stripe wallets on Payment Element, or direct PayPal SDK integration.

Why testing wallet payments matters

  • Revenue loss — wallet button visible but broken on Safari iOS; PayPal return URL drops session cookie; order never created while user saw success sheet.
  • Conversion — mobile wallet users are high-intent; fallback to card must work when wallet cancelled.
  • Support load — "Paid with Apple Pay but no confirmation email" usually means webhook gap, not wallet bug—still needs E2E proof.

Wallet APIs differ from card Elements—do not assume one spec covers all.

Complexity map

ScenarioEdge caseWhy tests breakApproach
Apple PayNot on Linux CISheet never opensmacOS job or probe-only PR path
Google PayChrome headlessButton hiddenHeaded Chrome or API Arrange
PayPal popupBlocked in headlessHang foreverpage.waitForEvent('popup') + sandbox login
PayPal redirectFull page leaveSession lostPersist cart id; probe after return
Stripe walletPayment Element mountSame iframe rulesframeLocator + wallet button visibility
Cancel walletUser dismisses sheetOrphan pending orderProbe no paid order until card fallback
Shipping in PRBRequired contact fieldsValidation flakeSeed address in Arrange
Domain verificationApple Pay domain fileWorks staging not prodSeparate smoke on prod domain
Currency/countryWallet PM availabilityButton missingSeed geo; document uncovered slices
WebhookSame as cardSuccess sheet ≠ paidProbe order status
Express checkoutGuest vs logged-inWrong customer linkProbe customer id on order
Refund wallet chargePM type specificRefund API differsSee returns guide

Tiered testing strategy

TierCoverageWhen
Probe + card pathOrder logic, webhooksEvery PR
PayPal sandbox redirectFull wallet flowPR if stable in CI
Apple Pay UISafari sheetmacOS nightly or manual
TrueCoveragewallet_type prod sharePrioritize tiers when slice grows

Stripe wallets (Apple Pay / Google Pay)

When using Stripe Payment Element with wallets enabled:

// Wallets may not appear in Linux CI — skip UI with test annotation
test('card fallback when wallet unavailable', async ({ page, request }) => {
await page.goto(`/checkout?runId=${runId}`);
const walletButton = page.getByRole('button', { name: /apple pay|google pay/i });
if (await walletButton.isVisible().catch(() => false)) {
test.info().annotations.push({ type: 'wallet', description: 'wallet visible—run on macOS for full flow' });
}
// Card path always runnable
const frame = page.frameLocator('iframe[name^="__privateStripeFrame"]').first();
await frame.getByLabel(/card number/i).fill('4242424242424242');
await page.getByRole('button', { name: 'Pay' }).click();
await expect.poll(async () => {
const res = await request.get(`/api/test/probes/orders/${runId}`);
return (await res.json()).status;
}).toBe('paid');
});

Stripe documents wallet testing in test mode—register test domain for Apple Pay on staging.

PayPal sandbox

Use PayPal sandbox buyer/seller accounts in CI secrets.

test('PayPal sandbox checkout', async ({ page, context, request }) => {
await page.goto(`/checkout?runId=${runId}`);
const popupPromise = page.waitForEvent('popup');
await page.getByRole('button', { name: 'PayPal' }).click();
const popup = await popupPromise;
await popup.getByLabel('Email').fill(process.env.PAYPAL_SANDBOX_BUYER!);
await popup.getByLabel('Password').fill(process.env.PAYPAL_SANDBOX_PASSWORD!);
await popup.getByRole('button', { name: 'Log In' }).click();
await popup.getByRole('button', { name: 'Pay Now' }).click();
await expect(page).toHaveURL(/order-confirmation/, { timeout: 60_000 });
await expect.poll(async () => {
const res = await request.get(`/api/test/probes/orders/${runId}`);
return (await res.json()).status;
}).toBe('paid');
});

Assert probe, not PayPal success page alone—your capture webhook must fire.

Fallback when wallet cancelled

await page.getByRole('button', { name: 'Apple Pay' }).click();
// Simulate dismiss — Esc or cancel if sheet appears in headed run
await page.keyboard.press('Escape');
await expect.poll(async () => {
const res = await request.get(`/api/test/probes/orders/${runId}`);
return (await res.json()).status;
}).not.toBe('paid');
// Complete with card

Arrange / Act / Assert summary

  1. Arrange — seed cart, runId, sandbox credentials; HTTPS origin (wallets require secure context).
  2. Act — shortest wallet path available in current runner; card fallback on Linux.
  3. Assert — probe order paid + payment method type if stored; webhook processed.

Anti-patterns

Anti-patternWhy it failsBetter approach
Only Apple Pay in suiteLinux CI skips foreverCard + probe every PR
Assert PayPal popup titleUI changesProbe order
No cancel-wallet testGhost pending ordersProbe unpaid after dismiss
Live PayPal in CIReal money riskSandbox only
Skip webhook AssertSheet success, no orderSame probe as card

Example scenario

Situation: Shopper taps Apple Pay then cancels the wallet sheet.

Expected outcome: No paid order; checkout remains completable via card.

Why UI-only automation breaks: Checkout page shows error state but backend created pending PaymentIntent.

  1. Arrange: Seed cart for runId on HTTPS staging or localhost with valid cert.
  2. Act: Open wallet sheet and cancel (headed/macOS).
  3. Assert: Probe order not paid; complete card path; probe paid.

TestChimp workflow: Compare wallet_type × device_class in TrueCoverage—prioritize macOS job if Apple Pay dominates prod.

Same Arrange/Act/Assert pattern as expired-coupon checkout.

Connect scenarios to your QA workflow

Capture business rules in markdown test plans and enforce them with seed routes and probe Assert. Link SmartTests with // @Scenario: for requirement traceability. Use /testchimp test on PRs; /testchimp explore on SmartTest paths for non-functional gaps (ExploreChimp).

External references

Frequently asked questions

Can I test Apple Pay in GitHub Actions Linux runners?

Apple Pay requires Safari on Apple hardware—most Linux CI cannot open the wallet sheet. Run a small macOS job for wallet UI, or rely on probe Assert for order logic and manual session capture for wallet UX.

How do I test PayPal checkout in Playwright?

Use PayPal sandbox credentials, handle popup or redirect flow with waitForEvent popup, and assert via backend probe—not PayPal UI alone. Store sandbox buyer login in CI secrets.

Should wallet tests use the same webhook Assert as cards?

Yes—wallet authorization success is not order fulfillment. Poll probe until paid after PayPal return or Stripe payment_intent.succeeded.

Google Pay button never appears in CI—what do I do?

Google Pay requires eligible browser, user, and merchant config. Test card fallback every PR; run headed Chrome wallet spec on scheduled macOS/Windows agents.

How do I test wallet cancel without flaking?

Probe that no paid order exists after dismiss, then complete card checkout in same spec. Run cancel path in headed mode where sheet exists.

Do I need HTTPS for wallet E2E?

Apple Pay and many wallet APIs require secure context. Use staging HTTPS, mkcert localhost, or Playwright webServer with TLS for local wallet dev tests.

How do we track wallet vs card usage in testing?

Compare wallet_type × device_class distributions in TrueCoverage. When prod wallet share grows, run /testchimp evolve to add macOS or PayPal scenarios. Link SmartTests with // @Scenario: tags.

Apply these patterns in your repo

Run `/testchimp init` to connect TestChimp to your repo, then `/testchimp test` on PRs to turn these patterns into maintained SmartTests. Use `/testchimp evolve` when you want to expand coverage as your app grows.

Start free on TestChimp · Book a demo