微服務(wù)從代碼到k8s部署應(yīng)有盡有系列(七、支付服務(wù))
我們用一個(gè)系列來(lái)講解從需求到上線(xiàn)、從代碼到k8s部署、從日志到監(jiān)控等各個(gè)方面的微服務(wù)完整實(shí)踐。
整個(gè)項(xiàng)目使用了go-zero開(kāi)發(fā)的微服務(wù),基本包含了go-zero以及相關(guān)go-zero作者開(kāi)發(fā)的一些中間件,所用到的技術(shù)棧基本是go-zero項(xiàng)目組的自研組件,基本是go-zero全家桶了。
實(shí)戰(zhàn)項(xiàng)目地址:https://github.com/Mikaelemmmm/go-zero-looklook
1、支付服務(wù)業(yè)務(wù)架構(gòu)圖

2、依賴(lài)關(guān)系
payment-api(支付api)
order-rpc(訂單rpc) payment-rpc(支付rpc) usercenter(用戶(hù)rpc)
payment-rpc(支付rpc)
mqueue-rpc(消息隊(duì)列)
order-rpc(訂單rpc)
mqueue-rpc(消息隊(duì)列) travel-rpc
usercenter(用戶(hù)rpc)
identity-rpc(授權(quán)認(rèn)證rpc)
3、微信支付舉例
3.1 創(chuàng)建支付預(yù)處理訂單
1、用戶(hù)在我們這邊創(chuàng)建完訂單之后,要去微信那邊創(chuàng)建預(yù)支付訂單
app/payment/cmd/api/desc/payment.api
// 支付服務(wù)v1版本的接口
@server(
prefix: payment/v1
group: thirdPayment
)
service payment {
@doc "第三方支付:微信支付"
@handler thirdPaymentwxPay
post /thirdPayment/thirdPaymentWxPay (ThirdPaymentWxPayReq) returns (ThirdPaymentWxPayResp)
...
}
app/payment/cmd/api/internal/logic/thirdPayment/thirdPaymentwxPayLogic.go
ThirdPaymentwxPay
見(jiàn)下圖,我們創(chuàng)建微信預(yù)支付訂單時(shí)候做了一次封裝,因?yàn)槲覀兤脚_(tái)后續(xù)支付業(yè)務(wù)肯定不止民宿支付訂單,肯定還會(huì)有其他的,比如我們后續(xù)可以推出商城,推出課程等,所以在這里使用switch做了個(gè)業(yè)務(wù)分類(lèi),目前我們只有民宿訂單,但是除了查詢(xún)業(yè)務(wù)不一樣,其他都一樣,我們把一樣的邏輯封裝起來(lái),所以我們繼續(xù)看封裝后的方法 createWxPrePayOrder

app/payment/cmd/api/internal/logic/thirdPayment/thirdPaymentwxPayLogic.go
createWxPrePayOrder
這里就是拿到用戶(hù)的登陸userId去換openid(這塊我們之前注冊(cè)登陸那里有小程序注冊(cè)登陸,那時(shí)候就獲取了openid),然后調(diào)用paymentRpc中的CreatePayment創(chuàng)建我們本地的支付流水單號(hào),再通過(guò)調(diào)用微信sdk-> svc.NewWxPayClientV3(這里是我基于go-zero封裝了一次,沒(méi)啥難度都能看懂) ,
然后在微信端創(chuàng)建了一個(gè)關(guān)聯(lián)我們本地流水單號(hào)的預(yù)支付訂單,返回給前端,前端通過(guò)js發(fā)起請(qǐng)求即可

3.2 微信支付回調(diào)
當(dāng)前端拿著我們給的微信預(yù)處理訂單發(fā)起支付,用戶(hù)輸入密碼支付成功后,微信服務(wù)器會(huì)回調(diào)我們服務(wù)器,回調(diào)地址在我們配置中填寫(xiě)的

這個(gè)回調(diào)地址,一定要填寫(xiě)我們支付api服務(wù)中的回調(diào)處理方法,也就是如下圖的接口,這樣我們才能接收到微信回調(diào)進(jìn)來(lái),我們才可以做后續(xù)處理。

微信回調(diào)回來(lái)之后,我們要處理回調(diào)邏輯,我們要調(diào)用verifyAndUpdateState 將我們流水單號(hào)改為已支付

我們來(lái)看看verifyAndUpdateState方法,我們要查詢(xún)單號(hào)是否存在,比對(duì)回調(diào)回來(lái)的金額與創(chuàng)建時(shí)候金額是否一致更新流水單號(hào)即可。這里不用在校驗(yàn)簽名了,前一步的sdk已經(jīng)做了處理了

這里還要給前端寫(xiě)一個(gè)輪訓(xùn)接口,前端用戶(hù)支付成功后前端不能以前端的微信返回結(jié)果為準(zhǔn),要通過(guò)后端提供的接口輪訓(xùn),判斷這個(gè)流水單是否真的是后端返回支付成功狀態(tài),如果這個(gè)接口返回成功才算成功,微信前端返回的不能作為依據(jù),因?yàn)槲⑿徘岸朔祷氐牟话踩话汩_(kāi)發(fā)都明白不知道的自己百度。
3.3 支付成功發(fā)送小程序模版消息
我們支付回調(diào)成功之后,會(huì)給用戶(hù)發(fā)送一個(gè)入駐碼,去了商家那里要展示這個(gè)碼,商家通過(guò)后臺(tái)核對(duì)碼,其實(shí)就是美團(tuán)的樣子,我們?nèi)ッ缊F(tuán)下單,美團(tuán)會(huì)給你個(gè)碼,用戶(hù)拿著這個(gè)碼去入住或者消費(fèi)等。
ok,回調(diào)成功,我們會(huì)調(diào)用pyamentRpc去修改當(dāng)前流水單狀態(tài)成功

我們來(lái)看看paymentRpc中做了什么,

前面是校驗(yàn),核心做了兩件事情,第一是更新?tīng)顟B(tài),第二向消息隊(duì)列發(fā)送了一條消息,我們看看消息隊(duì)列中對(duì)應(yīng)的代碼

可以看到我們使用了go-queue發(fā)送了一條kq消息到kafka,而不是asynq延遲消息,因?yàn)槲覀兿胱屗杏嗛喠嗽撝Ц稜顟B(tài)的業(yè)務(wù)都能收到此消息后做相應(yīng)的處理,雖然目前我們只有一個(gè)地方監(jiān)聽(tīng)做處理(發(fā)送小程序模版消息通知用戶(hù)支付成功),所以這里就是發(fā)了一條該支付流水相關(guān)信息到kafka中,這里跟之前訂單那里是一樣的只是添加消息到隊(duì)列,沒(méi)有處理,那我們看看order-mq中怎么處理的。

前面order一節(jié)已經(jīng)介紹了整個(gè)order-mq的運(yùn)作機(jī)制,這里不再多說(shuō)了,我們只說(shuō)kq這里
當(dāng)order-mq啟動(dòng)后,go-queue會(huì)監(jiān)聽(tīng)kafka中的消息

我們?cè)賮?lái)看下具體實(shí)現(xiàn) , 當(dāng)前面支付回調(diào)成功添加到kafka中時(shí)候,order-mq中kafka會(huì)接受消息,也就是PaymentUpdateStatusMq.Consume會(huì)接收到kafka的消息,然后反序列化數(shù)據(jù),傳遞給execService 執(zhí)行具體業(yè)務(wù),那execService中執(zhí)行了什么呢?
可以看到下方紅框內(nèi),一個(gè)是修改訂單狀態(tài)(非支付狀態(tài),訂單也有自己狀態(tài)),一個(gè)是發(fā)消息(短信、微信小程序模版消息)給用戶(hù)
app/order/cmd/mq/internal/mqs/kq/paymentUpdateStatus.go

修改訂單狀態(tài)的我們就不看了,我們可以來(lái)看看發(fā)送小程序模版消息,下方LiveStateDate\LiveEndDate之前調(diào)試寫(xiě)死的,這個(gè)直接改成方法傳遞過(guò)來(lái)的時(shí)間就好了,轉(zhuǎn)換一下
【注】用戶(hù)想收到小程序模版消息,必須在前端讓用戶(hù)授權(quán)才行,這是小程序必須的不是我們能控制的

這里發(fā)送消息我們也不是真正的調(diào)用微信sdk去發(fā)送消息,也是往消息隊(duì)列MqueueRpc中插入模版消息(其實(shí)這里也可以直接發(fā)),然后由message消息服務(wù)從kafka中取出來(lái)真正發(fā)送,是想所有的短信、email、微信等消息統(tǒng)一從這個(gè)服務(wù)發(fā)送出去,這個(gè)自己根據(jù)自己公司業(yè)務(wù)或者架構(gòu)去靈活設(shè)計(jì)吧,不一定非得這樣。
那我們說(shuō)到這里了就直接去看看message消息服務(wù)代碼吧

message業(yè)務(wù)中只有一個(gè)mq,因?yàn)樗恍枰猺pc、api,只需要定時(shí)從隊(duì)列去消息發(fā)送消息,所以它運(yùn)行邏輯跟order-mq一樣的,同樣適用serviceGroup管理

我們不細(xì)說(shuō)了,運(yùn)行邏輯可以去看訂單服務(wù)那一節(jié)的order-mq有細(xì)說(shuō),我們只看具體實(shí)現(xiàn)邏輯,go-queue從kafka隊(duì)列中取出每一條要發(fā)送的微信小程序模版消息數(shù)據(jù),然后反序列化交給execService去處理,我們來(lái)看看execService

execService 主要就是整合數(shù)據(jù),通過(guò)小程序sdk的client 發(fā)送給小程序即可,這里有個(gè)注意點(diǎn),小程序是可以區(qū)分環(huán)境的,是發(fā)送到線(xiàn)上小程序還是體驗(yàn)版小程序,在下方紅色框內(nèi)有做區(qū)分,實(shí)際這樣是不安全的 通過(guò)這種方式,最好搞到配置文件里,萬(wàn)一開(kāi)發(fā)環(huán)境有人搗亂改成formal,隨便發(fā)給別人openid就出事了,這個(gè)自己可以改改哈

4、小結(jié)
到這里基本上整體項(xiàng)目服務(wù)邏輯都差不多說(shuō)明完了,后續(xù)會(huì)介紹收集日志、監(jiān)控、部署等
項(xiàng)目地址
https://github.com/zeromicro/go-zero
歡迎使用 go-zero 并 star 支持我們!
推薦閱讀
