出海支付實戰:Google Play與Apple Store接入避坑指南
1. 引言
今天我們來聊聊錢,一個項目想要掙錢就離不開商業化,說白了就是讓用戶掏錢,所以,支付系統也是我們在項目開發中必不可少的一個關鍵模塊。
而在 APP 出海的征途上,海外支付堪稱"必渡之河"。
根據 Statista 2024 數據,Google Play 與 Apple Store 合計占據全球移動應用市場 83% 的營收份額,其支付系統(IAP)如同支付寶與微信支付在國內的地位。
本文聚焦于兩大主流平臺的接入實踐,解析跨境支付的特殊場景與解決方案,以及業務上可能遇到的一些坑。
2. 業務交互流程
支付系統的核心在于狀態機的精準運轉,下圖展示典型交互鏈路:
圖片
關鍵節點三要素:
- 商品預配:提前在支付平臺創建商品ID并同步至業務數據庫(建議通過 Admin API 自動化)
- 支付憑證雙驗證:客戶端獲取支付令牌后,需業務后臺二次驗證(蘋果建議使用 AppStoreServerAPI)
- 原始交易ID綁定:訂閱類訂單需持久化 original_transaction_id 作為續期憑證
由于業務需要不同的充值套餐和訂閱周期,所有我們還需要先提前在 Apple Store 及 Google Play 中配置商品,并且給商品創建一個唯一 ID,存儲在后臺數據庫中(這一步我們可以在運營后臺里調用 Apple Store 或者谷歌 Play 的接口,同時寫入到 DB)。
當用戶想要獲取商品時,客戶端首先到后臺拉取商品列表,然后用戶點擊充值/訂閱后,客戶端就帶著商品 ID 請求 Google Play/Apple Store 同時拉起支付頁面,當用戶支付完成后,客戶端把產品ID、支付 Token、交易 ID 等信息傳入后臺并創建訂單。
需注意的是,原始交易 ID 可以管理訂閱訂單中的續訂狀態,后臺需要存儲起來。
接著,業務后臺請求支付網關接口,來驗證訂單的有效性,確認無誤后開始下發用戶權益(如:充值后獲取 100 顆鉆石進行主播打賞,或者成為尊貴的 VIP 用戶)。
3. 海外支付的要點及難點
一些背景
Google 和 Apple 未提供完整的流程和狀態支持文檔,中文資料亦不完整,因此我們以官方文檔為準,采用測試驅動開發。
同時,支付平臺服務端的接口支持較落后,我們盡量選用 SDK 進行交互。由于 Google Play 和 Apple Store 的回調通知類型不一致,支付狀態設計成為難點。
支付狀態流轉
比如,Apple 訂閱的通知類型【NOTIFICATION_TYPE】分為以下幾種:
圖片
Google 的通知類型包括:
● 續訂開始/續訂恢復
● 訂閱取消/續訂取消
● 訂閱下單
● 訂閱保留(相當于停機保號)
● 訂閱重新開始
● 訂閱到期
結合實際業務,我們選用了都有的其中幾種支付狀態,如:完成訂單、取消、續訂失敗、退訂、續訂成功等。
一次性商品的購買狀態扭轉比較簡單:
圖片
訂閱類商品更復雜一些:
圖片
如上圖所示,在訂閱類支付時需要考慮用戶暫停/取消訂閱,也需要根據業務需要讓用戶自己覺得是否自動續訂,自動續訂時可能還要考慮用戶的賬戶余額是否充足等場景。
表結構
商品表和訂單表記錄了業務中的商品列表和用戶購買狀態。商品表需提前在三方支付平臺上配置,訂單表記錄用戶購買的商品狀態和三方支付平臺的交易憑據。
商品表
以下是商品表的主要結構(采用 Go 語言的結構體形式展現):
type Product struct {
ID uint `gorm:"primaryKey NOT NULL AUTO_INCREMENT"`
ProductId string `json:"product_id" gorm:"index:product_id_app_id_deleted_at;type:varchar(64) DEFAULT ''"`
ProductType int `json:"product_type" gorm:"comment:商品類型,0.未知類型/1.消耗型購買/2.非消耗型購買/3.自動訂閱/4.非自動訂閱;type:tinyint(4) DEFAULT 0"`
Name string `json:"name" gorm:"comment:商品名稱;type:varchar(64) DEFAULT ''"`
Description string `json:"description" gorm:"comment:商品描述;type:varchar(1024) DEFAULT ''"`
Price int `json:"price" gorm:"comment:商品價格,精度2位小數,用100倍存儲;type:bigint DEFAULT 0"`
TokenType int `json:"token_type" gorm:"comment:虛擬幣類型,0.鉆石;1.金幣;2.元寶;3.其它;type:tinyint(4) DEFAULT 0"`
TokenQuantity int `json:"token_quantity" gorm:"comment:虛擬幣數量;type:int(11) DEFAULT 0"`
SubscribeDurationDay int `json:"subscribe_duration_day" gorm:"comment:會員訂閱時長(天);type:int(11) DEFAULT 0"`
Weight int `json:"weight" gorm:"comment:權重,排序時從大到小,客戶端根據此字段進行商品排序;type:int(11) DEFAULT 0"`
ImageURL string `json:"image_url" gorm:"comment:商品圖片URL;type:varchar(1024) DEFAULT ''"`
IsAutoSubscribe int `json:"is_auto_subscribe" gorm:"comment:是否是自動續費,0.未知/1.自動續費/2.非自動續費;type:tinyint(2) DEFAULT 0"`
Platform int `json:"platform" gorm:"comment:支付平臺, 0-海外,1-微信支付,2-國內支付渠道;type:tinyint(2) DEFAULT 0"`
}商品表主要是記錄業務中的商品列表,需要提前在三方支付平臺上進行配置,下單時客戶端需帶著商品 ID 請求后臺服務器。
訂單表
訂單表的定義如下所示(采用 Go 語言的結構體形式展現):
type Order struct {
ID uint `gorm:"primaryKey NOT NULL AUTO_INCREMENT"`
OrderId string `json:"order_id" gorm:"index:order_id_deleted_at;type:varchar(64) DEFAULT ''"`
ProductId string `json:"product_id" gorm:"index:user_uuid_app_product_id_deleted_at;type:varchar(64) DEFAULT ''"`
TransactionId string `json:"transaction_id" gorm:"comment:交易ID,訂閱/續訂時使用;type:varchar(64) DEFAULT ''"`
OriginalTransactionId string `json:"original_transaction_id" gorm:"comment:原始交易ID,訂閱/續訂時該ID一致;type:varchar(64) DEFAULT ''"`
UserUuid string `json:"user_uuid" gorm:"index:user_uuid_app_product_id_deleted_at;comment:用戶的UUID;type:varchar(64) DEFAULT ''"`
PayChannel int `json:"pay_channel" gorm:"comment:支付渠道,0/1/2,GooglePay/ApplePay/PayPal;type:tinyint DEFAULT 0"`
PaymentState int `json:"payment_state" gorm:"comment:支付狀態,-1:處理中 0:初始化 1:已完成 2 取消 3 續訂失敗 4 退款 5 續訂成功;type:tinyint DEFAULT 0"`
RefundTime string `json:"refund_time" gorm:"comment:退款時間;type:varchar(32) DEFAULT ''"`
}訂單主要記錄用戶購買的商品狀態,比如權益下發到哪一步了,續訂套餐是否在續費狀態,以及三方支付平臺的一些交易憑據(如交易ID)等。
5. 常見問題
1)如何防止掉單
在支付系統中,最重要的是用戶權益。很多時候用戶明明已經下單并且付錢了,但是 VIP 沒有充上,鉆石沒有到賬,是用戶無法接受的。
一個很常見的 Case 是:用戶付錢后,斷開網絡連接,這時后臺系統沒有收到消息,該怎么處理?
這里我們做了兩步來保證:
- 客戶端補償策略:采用本地事務日志+斷點續傳設計。客戶端在劃賬請求的 ACK 之前先調用后臺接口生成訂單,如果用戶在支付后突然斷網,重新打開客戶端后會檢查當前是否存在未確認的劃賬請求,如果有就再調用一次后臺訂單再 ACK。同時后臺通過冪等性來保證用戶不會多次支付同一筆訂單;
- 業務系統雙保險告警處理:業務平臺接收到支付網關回調時,發現已有訂單就更新訂單狀態;沒有訂單就發告警,進行人工處理;
2)如何保證賬單和訂單正確性
在傳統的公司交易中,都會需要會計來對賬,將每月的賬單和收支金額總額對比,防止出現賬不對錢的壞賬。
所以,一方面為了保證訂單的有序性,我們在業務系統禁止隨意扭轉訂單狀態;另一方面我們在支付網關進行每天的定時對賬:
- 狀態機檢查裝置:每次觸發業務回調時,業務后臺都會判斷數據庫狀態和支付平臺的后臺狀態一致性,若是不一致,則判斷狀態是否可以扭轉,若是不能扭轉則告警出來;若是可以扭轉則更新 DB 里訂單的狀態;
- 服務端哨兵系統:每小時掃描未完結訂單與支付平臺對賬。支付網關每天定時比較昨日數據庫和支付平臺后臺的交易狀態差異,有差錯的部分進行告警。
6. 小結
海外支付的接入涉及復雜的流程和細致的狀態管理,通過合理的系統設計和流程優化,可以有效解決掉單和賬單對賬的問題,確保用戶權益得到保障。



























