Apple Pay with web components

Introducing Web Component Apple Pay

To simplify Apple Pay on the web, Jupico provides a reusable, framework-agnostic <apple-pay-jupico> component. You embed one script and drop a single tag to render the native Apple Pay button, handle merchant validation, present the sheet, and return a short-lived token (or payment payload) on success.

Prerequisites (like Stripe):

  • Serve over HTTPS.
  • Register your domain(s) to use Apple Pay on the Web.
  • Have an Apple Merchant ID and the required Apple Pay certificates.
  • Apple Pay only appears on eligible devices/browsers (Safari/iOS/macOS with Wallet set up).

🛠️ Step-by-Step Guide

1️⃣ Add Jupico’s JavaScript Library

Include the hosted bundle that registers the Apple Pay component:

<head>
  <meta charset="UTF-8" />
  <!-- 👇🏻 Add Jupico hosted script (Apple Pay button is included here or in a sibling bundle) -->
  <script src="https://sandbox-payments-hosted-pages.jupico.com/wc/tokenization-form.umd.js"></script>
  <!-- If your environment separates Apple Pay, use:
  <script src="https://sandbox-payments-hosted-pages.jupico.com/wc/apple-pay-jupico.umd.js"></script>
  -->
</head>

2️⃣ Include the Web Component

Use the <apple-pay-jupico> tag and pass the required properties.

<apple-pay-jupico
  id="apple-pay-btn"
  :sessions="{YOUR_SESSIONS_OBJECT}"
  :transactionComplete="handleApplePayAuthorization"
/>
// Vanilla JavaScript
document.addEventListener("DOMContentLoaded", function () {
  fetch("https://your-appserver.com/browser-session-authorize", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ publicKey: "YOUR_PUBLIC_KEY",
     "applePay": {
           "enabled": true,
           "amount": 23,
           "description": "test applePay"
       }
    })
    .then(res => res.json())
    .then(res => {
      const el = document.createElement("apple-pay-jupico");
      el.id = "apple-pay-btn";
      el.sessions = res?.data?.sessions;                // required
      el.transactionComplete = handleApplePayAuthorization; // required
      document.getElementById("apple-pay-container").appendChild(el);
    })
    .catch(err => console.error("Error:", err));
});

🔹 Required Properties

  • sessions: opaque authorization sessions object from your backend (don’t modify it).
  • transaction-complete: callback invoked when the Apple Pay sheet finishes (success/cancel/error).

Like Stripe’s web flow, the component performs merchant validation and presents the Apple Pay sheet on user click. Apple requires a user gesture to open the sheet.


3️⃣ (Optional) Manually Trigger From a Button

The component renders a native Apple Pay button and handles the click itself. If you prefer an external trigger, expose a handler that calls the component’s internal action (if available in your build):

<button id="pay-with-apple">Pay with Apple Pay</button>
<script>
  document.getElementById("pay-with-apple").addEventListener("click", () => {
    const el = document.getElementById("apple-pay-btn");
    // If your build exposes an imperative method (optional):
    el?._instance?.exposed?.present?.();
    // Otherwise, rely on the built-in Apple Pay button rendered by the component.
  });
</script>

4️⃣ Handle Completion (Success / Error)

Your callback receives the result of the Apple Pay authorization. On success, you’ll typically get a short-lived token (or an Apple Pay payment payload) to send to your server.

function handleApplePayAuthorization(event) {
  // Inspect to learn the exact payload shape for your environment
  console.log("🍏 Apple Pay result:", event);

  if (event?.status === "authorized" || event?.success === true) {
    const oneTimeToken =
      event.oneTimeToken || event.paymentToken || event.token;
    // Send to your backend to create the charge/authorization
    fetch("https://your-appserver.com/applepay/confirm", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ paymentToken: oneTimeToken }),
    })
      .then(r => r.json())
      .then(r => console.log("✅ Server confirmation:", r))
      .catch(err => console.error("❌ Server error:", err));
  } else if (event?.status === "canceled") {
    console.warn("⛔️ User canceled Apple Pay.");
  } else {
    console.error("❌ Apple Pay error:", event?.error || event);
  }
}

Server-side (like Stripe’s “Submit the payment” step): Always decide the amount on the server and complete the transaction there using the token you received.


🔹 Full Integration Code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Apple Pay (Web) – Jupico</title>

  <!-- Jupico hosted script (registers the web components) -->
  <script src="https://sandbox-payments-hosted-pages.jupico.com/wc/tokenization-form.umd.js" charset="utf-8"></script>
  <!-- Or, if separated in your env:
  <script src="https://sandbox-payments-hosted-pages.jupico.com/wc/apple-pay-jupico.umd.js" charset="utf-8"></script>
  -->
</head>
<body>
  <div id="apple-pay-container"></div>

  <script>
    document.addEventListener("DOMContentLoaded", function () {
      // 1) Get sessions from your backend (your server does the B2B authorize call)
      fetch("https://your-appserver.com/browser-session-authorize", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ publicKey: "YOUR_PUBLIC_KEY" }),
      })
      .then(response => response.json())
      .then(response => {
        // 2) Create the Apple Pay component
        const applePayEl = document.createElement("apple-pay-jupico");
        applePayEl.id = "apple-pay-btn";
        applePayEl.sessions = response?.data?.sessions;
        applePayEl.transactionComplete = handleApplePayAuthorization;

        document.getElementById("apple-pay-container").appendChild(applePayEl);
      })
      .catch(error => console.error("Init error:", error));

      // 3) Completion handler (success/cancel/error)
      function handleApplePayAuthorization(event) {
        console.log("handleApplePayAuthorization", event);

        if (event?.status === "authorized" || event?.success === true) {
          const token = event.oneTimeToken || event.paymentToken || event.token;
          // Complete payment on your server
          fetch("https://your-appserver.com/applepay/confirm", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ paymentToken: token }),
          })
          .then(r => r.json())
          .then(r => console.log("Server confirmation:", r))
          .catch(err => console.error("Server error:", err));
        } else if (event?.status === "canceled") {
          console.warn("User canceled Apple Pay");
        } else {
          console.error("Apple Pay error:", event?.error || event);
        }
      }
    });
  </script>
</body>
</html>

Notes & Parallels with Stripe

  • Availability checks: As with Stripe, Apple requires device support + a card in Wallet. Your component only renders/enables the button when Apple Pay is available (Safari/iOS/macOS).
  • Merchant validation & domain registration: Similar to Stripe’s flow, the provider handles merchant validation, but you must ensure your domains are registered and your Apple Merchant ID/certificates are correctly set up.
  • User gesture: Present the Apple Pay sheet from a direct user action (button click).
  • Server-side confirmation: Just like Stripe’s PaymentIntent step, finalize the payment on your server with the token emitted by transactionComplete.
  • Testing: Use an Apple Pay-capable device/browser and a properly configured sandbox setup. If the button doesn’t appear, check domain registration, HTTPS, device support, and session validity.

Vue snippet (matches your request)

<apple-pay-jupico
  :sessions="sessionsObject"
  :transactionComplete="handleApplePayAuthorization"
/>

<script setup>
function handleApplePayAuthorization(event) {
  console.log("🍏 Apple Pay:", event);
  if (event?.status === "authorized" || event?.success === true) {
    const token = event.oneTimeToken || event.paymentToken || event.token;
    // Send to backend
    // await fetch("/applepay/confirm", { method: "POST", body: JSON.stringify({ paymentToken: token }) })
  }
}
</script>

If you want, I can also add a tiny backend stub (Node/Express or your stack) that receives paymentToken and completes the Apple Pay charge/authorization.