戰(zhàn):從0到上線的四個(gè)小步與三次踩坑)
Medusa接入支付寶微信支付完整實(shí)戰(zhàn)從0到上線的四個(gè)小步與三次踩坑【免費(fèi)下載鏈接】medusaThe worlds most flexible commerce platform for agents and developers項(xiàng)目地址: https://gitcode.com/GitHub_Trending/me/medusa大促前夜運(yùn)營(yíng)曉雯盯著后臺(tái)的訂單流失報(bào)表發(fā)愁活動(dòng)頁(yè)曝光量翻了四倍可結(jié)算頁(yè)轉(zhuǎn)化率紋絲不動(dòng)。一查日志問(wèn)題出在支付環(huán)節(jié)——顧客選完商品跳轉(zhuǎn)到支付頁(yè)就卡住超過(guò)一半的人在這一步離開(kāi)。她不懂代碼只知道一句話我們的商城好像付不了款。這個(gè)商城跑在 Medusa 上。Medusa 自稱the worlds most flexible commerce platform for agents and developers它的支付系統(tǒng)是模塊化的Stripe 能跑支付寶、微信支付理論上也能跑——只是沒(méi)人接。接下來(lái)的一個(gè)月我從零開(kāi)始給這套開(kāi)源電商平臺(tái)接上了國(guó)內(nèi)移動(dòng)支付全程記錄了它真實(shí)的脾氣。這篇實(shí)戰(zhàn)記就是那段時(shí)間的濃縮版。兩條路擺在面前自己造輪子還是站在輪子上接手的第一件事不是寫(xiě)代碼而是開(kāi)會(huì)。技術(shù)負(fù)責(zé)人拋出一個(gè)經(jīng)典問(wèn)題支付這塊是自己寫(xiě)還是接入現(xiàn)成的支付 SDK對(duì)比維度自己造輪子用 Medusa 支付模塊擴(kuò)展開(kāi)發(fā)量要自己寫(xiě)訂單狀態(tài)機(jī)、對(duì)賬、回調(diào)重試只補(bǔ)支付網(wǎng)關(guān)的對(duì)接層維護(hù)成本每個(gè)接口變動(dòng)都要跟進(jìn)核心邏輯隨社區(qū)版本走風(fēng)險(xiǎn)點(diǎn)金額精度、回調(diào)亂序全靠自己兜需要先吃透它的約定時(shí)間動(dòng)輒數(shù)月幾天到一兩周結(jié)論很快出來(lái)支付網(wǎng)關(guān)的 API 是胳膊訂單與資金的流轉(zhuǎn)邏輯才是大腿。Medusa 已經(jīng)把大腿長(zhǎng)好了我們要做的只是給胳膊裝上去。動(dòng)手前先清點(diǎn)四樣?xùn)|西缺一不可一個(gè)能跑起來(lái)的 Medusa 項(xiàng)目版本與官方倉(cāng)庫(kù)保持同步避免 API 對(duì)不上支付寶開(kāi)放平臺(tái) / 微信商戶平臺(tái)的沙箱測(cè)試賬號(hào)提前把 AppID、商戶號(hào)、密鑰申請(qǐng)下來(lái)一臺(tái)有公網(wǎng)地址的開(kāi)發(fā)機(jī)或者一個(gè)內(nèi)網(wǎng)穿透工具——回調(diào)測(cè)試離不開(kāi)它一顆耐心支付調(diào)試的報(bào)錯(cuò)信息永遠(yuǎn)比你預(yù)想的更隱晦第一小步看懂 Medusa 支付模塊的插槽結(jié)構(gòu)Medusa 的支付模塊長(zhǎng)這樣核心邏輯在packages/modules/payment/src/services/下真正的插槽是payment-provider.ts里的PaymentProviderService它負(fù)責(zé)把外部支付服務(wù)取出來(lái)調(diào)用而每個(gè)支付服務(wù)要長(zhǎng)什么樣由packages/core/types/src/payment/provider.ts里的IPaymentProvider接口規(guī)定。這個(gè)接口像一份合同列明了所有需要履約的方法接口方法對(duì)應(yīng)業(yè)務(wù)動(dòng)作initiatePayment創(chuàng)建支付會(huì)話authorizePayment授權(quán)預(yù)扣款capturePayment確認(rèn)收款refundPayment退款retrievePayment/getPaymentStatus查詢支付狀態(tài)cancelPayment取消支付deletePayment清理支付數(shù)據(jù)換句話說(shuō)你不需要關(guān)心訂單怎么流轉(zhuǎn)只需要按這份合同把支付寶和微信的 API 翻譯成這些方法的實(shí)現(xiàn)。第二小步仿照 Stripe 寫(xiě)一個(gè)最小可跑的本地提供商Medusa 自帶了 Stripe 參考實(shí)現(xiàn)路徑在packages/modules/providers/payment-stripe/src/core/stripe-base.ts。它繼承了一個(gè)抽象基類AbstractPaymentProvider把 Stripe 的 PaymentIntent 封裝成 Medusa 認(rèn)得的支付會(huì)話。我照葫蘆畫(huà)瓢先寫(xiě)了一個(gè)假裝能支付的本地提供商——收到initiatePayment就返回一個(gè)假支付鏈接點(diǎn)開(kāi)就是成功頁(yè)。這一步不接任何真實(shí)網(wǎng)關(guān)目的是驗(yàn)證插槽本身通不通。class MockProviderService extends AbstractPaymentProviderOptions { static identifier payment-mock async initiatePayment({ amount }) { return { id: mock_${Date.now()}, data: { amount } } } async getPaymentStatus() { return PaymentSessionStatus.AUTHORIZED } }寫(xiě)完后記得看一個(gè)關(guān)鍵文件packages/modules/payment/src/loaders/providers.ts。它規(guī)定每個(gè)提供商必須以pp_{identifier}的形式注冊(cè)進(jìn)容器少一個(gè)static identifier都會(huì)在啟動(dòng)時(shí)直接報(bào)錯(cuò)。預(yù)期結(jié)果后臺(tái)的支付設(shè)置里能看到payment-mock這個(gè)選項(xiàng)下單能走到支付成功。第三小步把支付寶和微信的官方 SDK 接進(jìn)來(lái)插槽驗(yàn)證通過(guò)后真正的活才開(kāi)始。這一步把官方 SDK 的調(diào)用填進(jìn)剛才的骨架initiatePayment里發(fā)預(yù)下單請(qǐng)求拿到支付參數(shù)返回給前端拉起收銀臺(tái)capturePayment里根據(jù)回調(diào)結(jié)果確認(rèn)入賬。// medusa-config.js 中注冊(cè)你的支付提供商 modules: { payment: { providers: [ { resolve: ./src/providers/payment-alipay, options: { appId: process.env.ALIPAY_APP_ID, privateKey: process.env.ALIPAY_PRIVATE_KEY, alipayPublicKey: process.env.ALIPAY_PUBLIC_KEY } } ] } }微信支付那邊同理只是多了 JSAPI、APP、小程序幾種支付場(chǎng)景實(shí)現(xiàn)上就多幾個(gè)initiatePayment的分支。每一行配置背后都對(duì)應(yīng)商戶平臺(tái)上的一個(gè)設(shè)置項(xiàng)缺一個(gè)webhookSecret啟動(dòng)時(shí) Medusa 會(huì)不厭其煩地打警告。第四小步用 Webhook 把支付結(jié)果簽收回來(lái)支付回調(diào)可以理解成快遞簽收通知用戶付完錢支付寶/微信異步告訴你的服務(wù)器這筆單子成了。Medusa 只認(rèn)它自己的狀態(tài)機(jī)——PENDING → AUTHORIZED → CAPTURED所以回調(diào)到達(dá)后要做兩件事驗(yàn)簽然后把它翻譯成一次capturePayment。沙箱里最容易忽略的是這件事回調(diào)的地址必須公網(wǎng)可達(dá)。我第一次測(cè)試回調(diào)靜默丟失訂單永遠(yuǎn)停在待付款排查了半天才發(fā)現(xiàn)是內(nèi)網(wǎng)地址根本收不到通知。三次真實(shí)的踩坑記錄坑一金額單位對(duì)不上現(xiàn)象下單 100 元支付寶賬單顯示 1 元。原因支付寶以元為金額單位而 Medusa 內(nèi)部統(tǒng)一按最小貨幣單位分存儲(chǔ)我直接把a(bǔ)mount原樣傳了出去。解決參考 Stripe 提供商的getSmallestUnit工具入?yún)⑶皳Q算一次。這個(gè)坑賠了不止一杯奶茶??佣卣{(diào)驗(yàn)簽一直失敗現(xiàn)象Webhook 能收到但驗(yàn)簽 100% 報(bào)錯(cuò)。原因支付寶的驗(yàn)簽參數(shù)是 URL 編碼后的字符串直接用原始 body 去驗(yàn)順序全亂了。解決嚴(yán)格按官方文檔規(guī)定的參數(shù)拼接順序組裝驗(yàn)簽串先本地單測(cè)再聯(lián)調(diào)??尤诚溆脩魭叽a后無(wú)響應(yīng)現(xiàn)象App 內(nèi)拉起支付后頁(yè)面一直轉(zhuǎn)圈。原因沙箱環(huán)境只支持沙箱版客戶端真機(jī)上用了正式版支付寶自然查無(wú)此單。解決測(cè)試手機(jī)統(tǒng)一安裝沙箱專用 App所有測(cè)試賬號(hào)走同一套環(huán)境。沙箱環(huán)境隔離是最容易被忽略的隱形坑。上線前必查的五件事金額精度全鏈路核對(duì)一遍最小單位換算尤其是退款路徑反向換算最容易出錯(cuò)。回調(diào)冪等支付寶/微信可能重復(fù)通知同一筆訂單capturePayment必須對(duì)重復(fù)回調(diào)無(wú)副作用。日志留痕支付相關(guān)日志單獨(dú)建文件記錄請(qǐng)求簽名、原始回調(diào)報(bào)文出問(wèn)題能回溯。密鑰管理私鑰不進(jìn)代碼庫(kù)用環(huán)境變量或密鑰管理服務(wù)商戶證書(shū)設(shè)置有效期提醒?;貪L預(yù)案支付網(wǎng)關(guān)升級(jí)前保留舊版本的容器鏡像約定好觀察 15 分鐘失敗即回滾。還能怎么玩兩個(gè)進(jìn)階方向接完基礎(chǔ)支付只是開(kāi)始。第一個(gè)方向是多提供商并存——給支付會(huì)話配一個(gè)優(yōu)先級(jí)列表支付寶掛了自動(dòng)降級(jí)到微信結(jié)算頁(yè)的支付成功率報(bào)表會(huì)好看很多。第二個(gè)方向是把a(bǔ)ccount_holder能力用起來(lái)讓用戶綁定銀行卡或開(kāi)通免密支付復(fù)購(gòu)時(shí)一鍵扣款把支付從交易終點(diǎn)變成增長(zhǎng)杠桿。給你的行動(dòng)清單五件小事先跑通本地 Mock 提供商確認(rèn)插槽通暢再碰真實(shí)網(wǎng)關(guān)金額單位換算寫(xiě)成一個(gè)工具函數(shù)從第一天就全局復(fù)用Webhook 回調(diào)地址用公網(wǎng)環(huán)境沙箱手機(jī)裝專用測(cè)試客戶端每筆支付的關(guān)鍵日志打全包括驗(yàn)簽原始報(bào)文上線前完整走一遍支付-回調(diào)-退款-重復(fù)回調(diào)四連測(cè)接入支付這件事90% 的時(shí)間花在跟約定較勁上——跟 Medusa 的約定跟支付平臺(tái)的約定。把這層約定吃透了剩下的只是翻譯工作。而 Medusa 最值錢的地方恰恰是它把那些約定固化成了清晰的接口合同讓翻譯這件事變成了照本宣科?!久赓M(fèi)下載鏈接】medusaThe worlds most flexible commerce platform for agents and developers項(xiàng)目地址: https://gitcode.com/GitHub_Trending/me/medusa創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考