钱包结算所有, 第三方充值到钱包
This commit is contained in:
@@ -3,6 +3,7 @@ package payment
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@@ -109,11 +110,14 @@ func (h *Handler) LeshuaNotify(c *gin.Context) {
|
||||
c.String(http.StatusBadRequest, "FAIL")
|
||||
return
|
||||
}
|
||||
log.Printf("[payment] leshua notify received third_order_id=%s leshua_order_id=%s status=%s amount=%s", params["third_order_id"], params["leshua_order_id"], params["status"], params["amount"])
|
||||
result, err := h.service.HandleLeshuaNotify(params)
|
||||
if err != nil || result == nil || !result.OK {
|
||||
log.Printf("[payment] leshua notify failed third_order_id=%s err=%v", params["third_order_id"], err)
|
||||
c.String(http.StatusOK, "FAIL")
|
||||
return
|
||||
}
|
||||
log.Printf("[payment] leshua notify processed third_order_id=%s status=%s", params["third_order_id"], params["status"])
|
||||
c.String(http.StatusOK, result.Message)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
@@ -30,6 +31,13 @@ type Repository struct {
|
||||
isMockMode bool
|
||||
}
|
||||
|
||||
const (
|
||||
channelSourceCreate = "create"
|
||||
channelSourceQuery = "query"
|
||||
channelSourceNotify = "notify"
|
||||
channelSourceMock = "mock"
|
||||
)
|
||||
|
||||
func NewRepository(db *gorm.DB, cfg config.PaymentConfig, orderRepo *order.Repository, walletRepo *wallet.Repository) *Repository {
|
||||
provider := cfg.Provider
|
||||
if provider == "" {
|
||||
@@ -61,7 +69,7 @@ func (r *Repository) Start(userID uint64, orderID uint64, req StartPaymentReques
|
||||
"third_order_id": payment.ThirdOrderID,
|
||||
"leshua_order_id": payment.ProviderOrderID,
|
||||
"status": "2",
|
||||
}); err != nil {
|
||||
}, channelSourceMock); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
latest, err := r.findPaymentByID(payment.ID)
|
||||
@@ -103,7 +111,7 @@ func (r *Repository) Start(userID uint64, orderID uint64, req StartPaymentReques
|
||||
"jspay_url": resp.JSPayURL,
|
||||
"jspay_info": resp.JSPayInfo,
|
||||
"raw_request": jsonMap(rawReq),
|
||||
"raw_response": jsonMap(resp.Raw),
|
||||
"raw_response": jsonMap(withRawSource(resp.Raw, channelSourceCreate)),
|
||||
}).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -130,7 +138,7 @@ func (r *Repository) StartWalletRecharge(userID uint64, req WalletRechargePaymen
|
||||
"third_order_id": payment.ThirdOrderID,
|
||||
"leshua_order_id": payment.ProviderOrderID,
|
||||
"status": "2",
|
||||
}); err != nil {
|
||||
}, channelSourceMock); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
latest, err := r.findPaymentByID(payment.ID)
|
||||
@@ -167,7 +175,7 @@ func (r *Repository) StartWalletRecharge(userID uint64, req WalletRechargePaymen
|
||||
"jspay_url": resp.JSPayURL,
|
||||
"jspay_info": resp.JSPayInfo,
|
||||
"raw_request": jsonMap(rawReq),
|
||||
"raw_response": jsonMap(resp.Raw),
|
||||
"raw_response": jsonMap(withRawSource(resp.Raw, channelSourceCreate)),
|
||||
}).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -195,7 +203,7 @@ func (r *Repository) QueryWalletRecharge(userID uint64, paymentID uint64) (*Paym
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.applyChannelStatus(&payment, resp.Status, resp.PayTime, resp.Raw); err != nil {
|
||||
if err := r.applyChannelStatus(&payment, resp.Status, resp.PayTime, resp.Raw, channelSourceQuery); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
latest, err := r.findPaymentByID(payment.ID)
|
||||
@@ -222,7 +230,7 @@ func (r *Repository) Query(userID uint64, orderID uint64) (*PaymentDTO, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.applyChannelStatus(&payment, resp.Status, resp.PayTime, resp.Raw); err != nil {
|
||||
if err := r.applyChannelStatus(&payment, resp.Status, resp.PayTime, resp.Raw, channelSourceQuery); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
latest, err := r.findPaymentByID(payment.ID)
|
||||
@@ -234,8 +242,19 @@ func (r *Repository) Query(userID uint64, orderID uint64) (*PaymentDTO, error) {
|
||||
}
|
||||
|
||||
func (r *Repository) HandleLeshuaNotify(params map[string]string) (*NotifyResult, error) {
|
||||
if !r.isMockMode && !r.leshua.VerifyNotify(params) {
|
||||
return nil, ErrPaymentVerifyFailed
|
||||
if !r.isMockMode {
|
||||
verify := r.leshua.VerifyNotifyDetail(params)
|
||||
if !verify.OK {
|
||||
log.Printf(
|
||||
"[payment] leshua notify verify failed third_order_id=%s got=%s expected=%v keys=%v",
|
||||
params["third_order_id"],
|
||||
shortSign(verify.Got),
|
||||
shortExpectedSigns(verify.Expected),
|
||||
verify.ParamKeys,
|
||||
)
|
||||
return nil, ErrPaymentVerifyFailed
|
||||
}
|
||||
log.Printf("[payment] leshua notify verified third_order_id=%s matched_key=%s", params["third_order_id"], verify.MatchedKey)
|
||||
}
|
||||
thirdOrderID := params["third_order_id"]
|
||||
if thirdOrderID == "" {
|
||||
@@ -251,7 +270,7 @@ func (r *Repository) HandleLeshuaNotify(params map[string]string) (*NotifyResult
|
||||
if amount := parseCent(params["amount"]); amount > 0 && amount != payment.AmountCent {
|
||||
return nil, ErrPaymentVerifyFailed
|
||||
}
|
||||
if err := r.applyChannelStatus(&payment, params["status"], params["pay_time"], params); err != nil {
|
||||
if err := r.applyChannelStatus(&payment, params["status"], params["pay_time"], params, channelSourceNotify); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &NotifyResult{OK: true, Message: "000000"}, nil
|
||||
@@ -366,7 +385,7 @@ func (r *Repository) createWalletRechargePayment(userID uint64, amountCent int64
|
||||
return &payment, nil
|
||||
}
|
||||
|
||||
func (r *Repository) applyChannelStatus(payment *model.PaymentOrder, status string, payTime string, raw map[string]string) error {
|
||||
func (r *Repository) applyChannelStatus(payment *model.PaymentOrder, status string, payTime string, raw map[string]string, source string) error {
|
||||
switch status {
|
||||
case "2", "30":
|
||||
paidAt := parseLeshuaTime(payTime)
|
||||
@@ -374,29 +393,28 @@ func (r *Repository) applyChannelStatus(payment *model.PaymentOrder, status stri
|
||||
now := time.Now()
|
||||
paidAt = &now
|
||||
}
|
||||
return r.confirmPaid(payment, status, *paidAt, raw)
|
||||
return r.confirmPaid(payment, status, *paidAt, raw, source)
|
||||
case "6":
|
||||
return r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
|
||||
"status": "closed",
|
||||
"raw_response": jsonMap(raw),
|
||||
"notified_at": time.Now(),
|
||||
}).Error
|
||||
return r.updateChannelStatus(payment.ID, "closed", raw, source)
|
||||
case "8":
|
||||
return r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
|
||||
"status": "failed",
|
||||
"raw_response": jsonMap(raw),
|
||||
"notified_at": time.Now(),
|
||||
}).Error
|
||||
return r.updateChannelStatus(payment.ID, "failed", raw, source)
|
||||
default:
|
||||
return r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
|
||||
"status": "paying",
|
||||
"raw_response": jsonMap(raw),
|
||||
"notified_at": time.Now(),
|
||||
}).Error
|
||||
return r.updateChannelStatus(payment.ID, "paying", raw, source)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repository) confirmPaid(payment *model.PaymentOrder, status string, paidAt time.Time, raw map[string]string) error {
|
||||
func (r *Repository) updateChannelStatus(paymentID uint64, status string, raw map[string]string, source string) error {
|
||||
updates := map[string]any{
|
||||
"status": status,
|
||||
"raw_response": jsonMap(withRawSource(raw, source)),
|
||||
}
|
||||
if source == channelSourceNotify {
|
||||
updates["notified_at"] = time.Now()
|
||||
}
|
||||
return r.db.Model(&model.PaymentOrder{}).Where("id = ?", paymentID).Updates(updates).Error
|
||||
}
|
||||
|
||||
func (r *Repository) confirmPaid(payment *model.PaymentOrder, status string, paidAt time.Time, raw map[string]string, source string) error {
|
||||
if payment.Status != "paid" {
|
||||
if payment.OrderID == 0 {
|
||||
if r.walletRepo == nil {
|
||||
@@ -414,13 +432,16 @@ func (r *Repository) confirmPaid(payment *model.PaymentOrder, status string, pai
|
||||
}
|
||||
}
|
||||
}
|
||||
return r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(map[string]any{
|
||||
updates := map[string]any{
|
||||
"status": "paid",
|
||||
"provider_order_id": firstNonEmpty(raw["leshua_order_id"], payment.ProviderOrderID),
|
||||
"raw_response": jsonMap(raw),
|
||||
"raw_response": jsonMap(withRawSource(raw, source)),
|
||||
"paid_at": paidAt,
|
||||
"notified_at": time.Now(),
|
||||
}).Error
|
||||
}
|
||||
if source == channelSourceNotify {
|
||||
updates["notified_at"] = time.Now()
|
||||
}
|
||||
return r.db.Model(&model.PaymentOrder{}).Where("id = ?", payment.ID).Updates(updates).Error
|
||||
}
|
||||
|
||||
func (r *Repository) markPaymentFailed(paymentID uint64, raw map[string]string, message string) error {
|
||||
@@ -498,6 +519,18 @@ func jsonMap(value map[string]string) datatypes.JSON {
|
||||
return datatypes.JSON(raw)
|
||||
}
|
||||
|
||||
func withRawSource(raw map[string]string, source string) map[string]string {
|
||||
out := map[string]string{}
|
||||
for key, value := range raw {
|
||||
out[key] = value
|
||||
}
|
||||
if source != "" {
|
||||
out["_source"] = source
|
||||
}
|
||||
out["_recorded_at"] = time.Now().Format(time.RFC3339)
|
||||
return out
|
||||
}
|
||||
|
||||
func newPaymentNo() (string, error) {
|
||||
buf := make([]byte, 4)
|
||||
if _, err := rand.Read(buf); err != nil {
|
||||
@@ -514,3 +547,21 @@ func firstNonEmpty(values ...string) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func shortExpectedSigns(values map[string]string) map[string]string {
|
||||
out := map[string]string{}
|
||||
for key, value := range values {
|
||||
out[key] = shortSign(value)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func shortSign(value string) string {
|
||||
if value == "" {
|
||||
return ""
|
||||
}
|
||||
if len(value) <= 12 {
|
||||
return value
|
||||
}
|
||||
return value[:12] + "..."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user