You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于构建类Stripe、Paystack的基于Iframe的支付JavaScript SDK的技术咨询

Hey there! Let's break down how to build an iframe-based payment SDK like Stripe or Paystack—this is totally achievable even if you haven't built an SDK before. I'll walk you through the core concepts and step-by-step implementation, using React/Vue for the payment UI since you're familiar with those frameworks.


Core Architecture Overview

First, let's clarify the big picture: an iframe-based payment SDK has three key parts:

  1. Merchant-side SDK: The small JS library merchants embed in their websites. It handles creating the iframe, passing payment parameters, and listening for payment status updates.
  2. Payment UI (iframe content): Your React/Vue-powered payment form hosted on your domain. This isolates sensitive payment data (like card details) from the merchant's site, which is critical for PCI compliance.
  3. Cross-domain communication layer: Uses window.postMessage to safely send data between the merchant's page and your iframe (since they're on different domains).

Step 1: Build the Merchant-Side SDK

This SDK needs to be lightweight and compatible with any website (so stick to vanilla JS for the core—no framework dependencies). Here's a simplified implementation:

Basic SDK Code

class PaymentSDK {
  static initialize(options) {
    // Validate required options first
    if (!options.amount || !options.merchantId || !options.onSuccess) {
      throw new Error("Missing required options: amount, merchantId, onSuccess");
    }

    // 1. Generate a unique session ID (or fetch one from your backend for better security)
    const sessionId = `session_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;

    // 2. Create and style the iframe (modal-like overlay)
    const iframe = document.createElement("iframe");
    iframe.src = `https://your-payment-domain.com/pay?sessionId=${sessionId}&amount=${options.amount}&merchantId=${options.merchantId}`;
    iframe.style.cssText = `
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      border: none;
      z-index: 99999;
      background: rgba(0,0,0,0.5);
    `;

    // Add sandbox restrictions for security
    iframe.sandbox = "allow-scripts allow-forms allow-top-navigation";
    document.body.appendChild(iframe);

    // 3. Listen for messages from the iframe
    const messageListener = (event) => {
      // Verify message origin to prevent malicious requests
      if (event.origin !== "https://your-payment-domain.com") return;

      const { type, data } = event.data;
      switch (type) {
        case "payment_success":
          options.onSuccess(data);
          cleanup();
          break;
        case "payment_failure":
          options.onFailure?.(data);
          cleanup();
          break;
        case "payment_cancel":
          options.onCancel?.();
          cleanup();
          break;
      }
    };

    window.addEventListener("message", messageListener);

    // Cleanup function to remove iframe and listener
    function cleanup() {
      document.body.removeChild(iframe);
      window.removeEventListener("message", messageListener);
    }
  }
}

// Expose to global scope for easy use by merchants
window.PaymentSDK = PaymentSDK;

How Merchants Use It

They'll embed your SDK and call it like this:

<script src="https://your-sdk-domain.com/payment-sdk.js"></script>
<script>
  PaymentSDK.initialize({
    amount: 99.99,
    merchantId: "merchant_123",
    onSuccess: (paymentData) => {
      console.log("Payment successful!", paymentData);
      // Update their UI or confirm order
    },
    onFailure: (error) => {
      console.error("Payment failed:", error);
    },
    onCancel: () => {
      console.log("Payment cancelled");
    }
  });
</script>

Step 2: Build the Payment UI (Iframe Content)

Use React or Vue to build the payment form. This runs on your domain, so it's isolated from the merchant's site. Here's a React example:

React Payment Form Snippet

import { useState, useEffect } from "react";
import axios from "axios";

function PaymentForm() {
  const [sessionId] = useState(new URLSearchParams(window.location.search).get("sessionId"));
  const [paymentDetails, setPaymentDetails] = useState(null);
  const [cardNumber, setCardNumber] = useState("");
  const [expiry, setExpiry] = useState("");
  const [cvv, setCvv] = useState("");

  // Fetch payment details from your backend using the session ID
  useEffect(() => {
    axios.get(`/api/payment/sessions/${sessionId}`)
      .then(res => setPaymentDetails(res.data))
      .catch(err => console.error("Failed to load payment details:", err));
  }, [sessionId]);

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      // Submit payment to your backend (never send card data directly to the merchant!)
      const response = await axios.post("/api/payment/process", {
        sessionId,
        cardNumber,
        expiry,
        cvv
      });

      // Send success message to the merchant's page
      window.parent.postMessage(
        { type: "payment_success", data: response.data },
        "*" // Replace with specific merchant domains for better security
      );
    } catch (err) {
      // Send failure message
      window.parent.postMessage(
        { type: "payment_failure", data: err.response?.data || "Payment failed" },
        "*"
      );
    }
  };

  const handleCancel = () => {
    window.parent.postMessage({ type: "payment_cancel" }, "*");
  };

  if (!paymentDetails) return <div className="loading">Loading payment details...</div>;

  return (
    <div className="payment-modal">
      <div className="payment-form-container">
        <h2>Pay ${paymentDetails.amount.toFixed(2)}</h2>
        <form onSubmit={handleSubmit}>
          <div className="form-group">
            <label>Card Number</label>
            <input
              type="text"
              value={cardNumber}
              onChange={(e) => setCardNumber(e.target.value)}
              placeholder="1234 5678 9012 3456"
              required
            />
          </div>
          <div className="form-group">
            <label>Expiry (MM/YY)</label>
            <input
              type="text"
              value={expiry}
              onChange={(e) => setExpiry(e.target.value)}
              placeholder="MM/YY"
              required
            />
          </div>
          <div className="form-group">
            <label>CVV</label>
            <input
              type="text"
              value={cvv}
              onChange={(e) => setCvv(e.target.value)}
              placeholder="123"
              required
            />
          </div>
          <div className="button-group">
            <button type="submit" className="pay-btn">Pay Now</button>
            <button type="button" className="cancel-btn" onClick={handleCancel}>Cancel</button>
          </div>
        </form>
      </div>
    </div>
  );
}

export default PaymentForm;

Step 3: Critical Security Best Practices

  • Validate message origins: Always check event.origin in the merchant-side SDK to ensure messages only come from your payment domain.
  • Avoid exposing sensitive data: Never send card details or payment tokens via postMessage—handle all sensitive data directly in the iframe, sending only non-sensitive status updates to the merchant.
  • Use HTTPS everywhere: Both your SDK and payment UI must be served over HTTPS to prevent man-in-the-middle attacks.
  • Sandbox the iframe: Use the sandbox attribute to restrict iframe permissions (as shown in the SDK code) to minimize risk if the iframe is compromised.
  • Encrypt session data: Instead of passing raw parameters in the iframe URL, have merchants fetch a signed session ID from your backend first—this prevents tampering with payment amounts or merchant IDs.

Step 4: Deployment & Distribution

  • Bundle the SDK: Use Webpack or Rollup to bundle your vanilla JS SDK into a UMD file, so it works with both script tags and module systems (ES modules, CommonJS).
  • Host the payment UI: Deploy your React/Vue payment form to a dedicated domain (e.g., pay.your-domain.com).
  • Document everything: Create clear docs for merchants showing how to embed the SDK, pass options, and handle callbacks.

Advanced Optimizations

  • Customizable styling: Allow merchants to pass style options (e.g., button color, modal size) that your SDK applies to the iframe, or let the payment UI fetch styling from your backend based on the merchant ID.
  • Error handling & timeouts: Add timeout logic in the SDK to close the iframe if no payment status is received after a set time.
  • Localization: Support multiple languages by letting merchants pass a language option, which the payment UI uses to render text.

内容的提问来源于stack exchange,提问作者Peril

火山引擎 最新活动