在 iOS / Android App 收款

App 內開啟 ReBate Pay 結帳頁、付款、自動回到 App;卡號與 3D 驗證都在結帳頁完成,App 不碰卡號。

檢視 Markdown

App 收款沿用網頁的 Hosted Checkout 與 Payment Link:你的後端建立一次性付款連結,App 用系統的 App 內瀏覽器開啟,付款完成後結帳頁把顧客送回你的 App。

API 金鑰只能放在你的後端。 不要把 rbp_... 金鑰打包進 App——任何人都能從 App 檔案取出來。App 只跟你自己的後端溝通。

流程

  1. 1在主控台「開發者 → App 串接」登記你 App 的網址 scheme,例如 com.example.shop。
  2. 2App 請你的後端建立訂單;後端用 API 金鑰呼叫 POST /api/PaymentLink(singleUse: true),successUrl 填 com.example.shop://checkout/done。
  3. 3後端把完整網址(REBATE_PAY_BASE_URL + 回應的 url)交給 App。
  4. 4App 用 ReBate Pay SDK 開啟:iOS 是 ASWebAuthenticationSession,Android 是 Chrome Custom Tabs。顧客在結帳頁刷卡、完成 3D 驗證。
  5. 5付款成功後,結帳頁導向 com.example.shop://checkout/done?rebatepay_checkout=chks_…&rebatepay_status=paid,SDK 把結果交回 App。
  6. 6App 把 rebatepay_checkout 交給後端;後端用 GET /api/Checkout/{id} 確認這筆結帳屬於該訂單的付款連結且 paid 為 true(或收到 payment.succeeded Webhook),再出貨或開通。

1. 登記 App 網址 scheme

只有登記過的 scheme 能當 successUrl,其他非 https 的網址都會被拒絕(VALIDATION_ERROR)。最多 5 個;建議用反向網域(com.example.shop),避免和其他 App 衝突。http、https、tel、mailto、intent 等系統或網頁用的 scheme 不能登記。移除某個 scheme 後,使用它的付款連結付款完成就不會再導回 App(會停在 ReBate Pay 的完成頁)。

curl -sS -X POST "$REBATE_PAY_BASE_URL/api/PaymentLink" \
  -H "Authorization: Bearer $REBATE_PAY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order_1042" \
  -d '{
    "name": "Order #1042",
    "priceId": "price_...",
    "singleUse": true,
    "successUrl": "com.example.shop://checkout/done?order=1042"
  }'
# => { "id": "plink_...", "url": "/pay/<token>", ... }
# Give the app: $REBATE_PAY_BASE_URL + url

successUrl 可以帶你自己的參數(例如 ?order=1042),ReBate Pay 會保留它們,再加上 rebatepay_checkout 與 rebatepay_status(paid 或試用的 trialing)。

3. App 開啟結帳

// Package: packages/rebatepay-ios (Swift Package "RebatePay", iOS 13+)
import RebatePay
import UIKit

final class CheckoutViewController: UIViewController {
    private let checkout = RebatePayCheckout(callbackScheme: "com.example.shop")

    func pay(orderId: String) async {
        do {
            let paymentURL = try await api.createCheckout(orderId: orderId) // your backend
            let result = try await checkout.present(paymentURL: paymentURL, from: view.window!)
            switch result {
            case .completed(let checkoutId, _):
                // Don't trust the app side: your backend checks GET /api/Checkout/{id}.
                showReceipt(try await api.confirmOrder(orderId: orderId, checkoutId: checkoutId))
            case .canceled:
                showMessage("Payment was not completed")
            }
        } catch {
            showMessage("Could not open checkout: \(error)")
        }
    }
}
iOSAndroid
SDKpackages/rebatepay-ios(Swift Package RebatePay)packages/rebatepay-android(模組 :checkout)
最低版本iOS 13Android 6.0(API 23)
開啟方式ASWebAuthenticationSession(預設不共用 Safari cookie,不跳「要使用…登入」提示)Chrome Custom Tabs;沒有支援的瀏覽器時退回預設瀏覽器
需要的 App 設定不需要設定 URL Types,系統直接攔截回傳manifestPlaceholders["rebatepayScheme"] 設成你的 scheme
顧客關掉結帳.canceledRebatePayResult.Canceled
付款成功後結帳頁會在幾秒後自動回到 App;有些 Android 瀏覽器會擋住「沒有點擊就跳到 App」,這時顧客點畫面上的「返回商店」按鈕即可。

4. 後端確認付款

App 收到的 rebatepay_status 只代表結帳頁導回了 App,任何人都能手動打開 com.example.shop://checkout/done?rebatepay_status=paid。一律由後端確認後才出貨或開通。

GET/api/Checkout/{id}— 查詢結帳與付款狀態(需要 payment_links:read)
// POST /orders/:id/confirm — the app sends the rebatepay_checkout value it got back
app.post("/orders/:id/confirm", requireUser, async (req, res) => {
  const order = await db.orders.find(req.params.id, req.user.id);
  const r = await fetch(`${process.env.REBATE_PAY_BASE_URL}/api/Checkout/${req.body.checkoutId}`, {
    headers: { Authorization: `Bearer ${process.env.REBATE_PAY_API_KEY}` },
  });
  if (!r.ok) return res.status(400).json({ paid: false });
  const { data: checkout } = await r.json();
  // Must be this order's link, and actually paid.
  const paid = checkout.paymentLinkId === order.paymentLinkId && checkout.paid === true;
  if (paid) await db.orders.markPaid(order.id, checkout.payment?.id);
  res.json({ paid });
});
// Also mark the order paid from the payment.succeeded webhook — whichever arrives first wins.

付款連結的 completionCount 在顧客送出結帳時就會增加,付款可能仍在處理或被拒絕,不能拿來判斷是否付款成功,請看 paid。同時訂閱 Webhook 的 payment.succeeded:顧客就算付款後沒回到 App(關掉了、手機沒電),你的後端一樣會收到通知。

不支援的做法

  • 把結帳頁放進 App 內的 WebView:結帳頁禁止被嵌入(frame-ancestors 'none'),3D 驗證的跳轉也會出問題。請用上面的 SDK。
  • 在 App 裡輸入卡號後呼叫 API:卡號只能在結帳頁輸入,開發者 API 會拒絕原始卡號。
  • 在 App 裡直接呼叫 Apple Pay/Google Pay:行動支付由結帳頁提供(支援的裝置會自動顯示按鈕,僅限單次付款),App 不需要也不能自己串接。
上架規定:App 內使用的數位內容與訂閱(會員、點數、解鎖功能),Apple App Store 與 Google Play 通常要求使用它們的內購;實體商品與實體服務一般可以使用第三方金流。上架前請確認你的商品屬於哪一類。