feat: import services and standardize Go 1.26.5

This commit is contained in:
2026-08-09 10:43:45 +08:00
parent 689c74b28f
commit 86024fa81d
1456 changed files with 220765 additions and 2 deletions

View File

@@ -0,0 +1,70 @@
package alipay
import (
"context"
"git.apinb.com/bsm-finance/wallet/internal/config"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/alipay"
)
type AliPay struct {
ctx context.Context
Client *alipay.Client
Body *gopay.BodyMap
}
func NewAlipay() (*AliPay, error) {
// 初始化支付宝客户端
// appid应用ID
// privateKey应用私钥支持PKCS1和PKCS8
// isProd是否是正式环境
client, err := alipay.NewClient(config.Spec.Alipay.AppID, config.Spec.Alipay.PrivateKey, config.Spec.Alipay.IsProd)
if err != nil {
return nil, err
}
// 打开Debug开关输出日志默认关闭
client.DebugSwitch = gopay.DebugOn
// 设置支付宝请求 公共参数
client.SetLocation(alipay.LocationShanghai) // 设置时区,不设置或出错均为默认服务器时间
client.SetCharset(alipay.UTF8) // 设置字符编码,不设置默认 utf-8
client.SetSignType(alipay.RSA2) // 设置签名类型,不设置默认 RSA2
client.SetReturnUrl(config.Spec.Alipay.ReturnURL) // 设置返回URL
client.SetNotifyUrl(config.Spec.Alipay.NotifyURL) // 设置异步通知URL
// 自动同步验签(只支持证书模式)
// 传入 alipayCertPublicKey_RSA2.crt 内容
client.AutoVerifySign([]byte(config.Spec.Alipay.PublicKeyRsa2Byte))
// 公钥证书模式
err = client.SetCertSnByPath(config.Spec.Alipay.CertPublicKey, config.Spec.Alipay.CertRoot, config.Spec.Alipay.CertPublicKeyRsa2)
if err != nil {
return nil, err
}
return &AliPay{
Client: client,
// ctx: context.Background(),
}, nil
}
func (srv *AliPay) SetBody(body, orderNo string, amount int64) {
srv.Body.Set("subject", config.Spec.Wallet.Name).
Set("body", body). //finance_payment表的identity
Set("product_code", "QUICK_WAP_WAY").
Set("out_trade_no", orderNo).
Set("total_amount", amount/100).
Set("timeout_express", "60m").
Set("quit_url", config.Spec.Alipay.QuitURL)
}
func (srv *AliPay) TradeWapPay() (string, error) {
result, err := srv.Client.TradeWapPay(srv.ctx, *srv.Body)
return result, err
}
func (srv *AliPay) TradeAppPay() (string, error) {
result, err := srv.Client.TradeAppPay(srv.ctx, *srv.Body)
return result, err
}

View File

@@ -0,0 +1,23 @@
package alipay
import (
"context"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/service"
)
// 支付宝APP支付
func AppPay(ctx context.Context, in *pb.AlipayTradeAppPayRequest) (reply *pb.AlipayTradeAppPayReply, err error) {
// parse authorization meta.
_, err = service.ParseMetaCtx(ctx, nil)
if err != nil {
return nil, err
}
// TODO: valid code
// TODO: add your logic code & delete this line.
return
}

View File

@@ -0,0 +1,23 @@
package alipay
import (
"context"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/service"
)
// 支付宝电脑网站支付
func PagePay(ctx context.Context, in *pb.AlipayTradePagePayRequest) (reply *pb.AlipayTradePagePayReply, err error) {
// parse authorization meta.
_, err = service.ParseMetaCtx(ctx, nil)
if err != nil {
return nil, err
}
// TODO: valid code
// TODO: add your logic code & delete this line.
return
}

View File

@@ -0,0 +1,23 @@
package alipay
import (
"context"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/service"
)
// 支付宝转账到个人支付宝账户
func Transfer(ctx context.Context, in *pb.AlipayUniTransferRequest) (reply *pb.AlipayUniTransferReply, err error) {
// parse authorization meta.
_, err = service.ParseMetaCtx(ctx, nil)
if err != nil {
return nil, err
}
// TODO: valid code
// TODO: add your logic code & delete this line.
return
}

View File

@@ -0,0 +1,23 @@
package alipay
import (
"context"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/service"
)
// 支付宝wap支付
func WapPay(ctx context.Context, in *pb.AlipayTradeWapPayRequest) (reply *pb.AlipayTradeWapPayReply, err error) {
// parse authorization meta.
_, err = service.ParseMetaCtx(ctx, nil)
if err != nil {
return nil, err
}
// TODO: valid code
// TODO: add your logic code & delete this line.
return
}

View File

@@ -0,0 +1,62 @@
package basic
import (
"context"
"time"
"git.apinb.com/bsm-finance/wallet/internal/impl"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/utils"
"git.apinb.com/bsm-sdk/core/vars"
)
// 添加银行卡
func AddBankCard(ctx context.Context, in *pb.AddBankCardRequest) (reply *pb.StatusReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
if in.Phone == "" || in.IdCard == "" || in.CardNo == "" || in.CardOwner == "" {
return nil, errcode.ErrInvalidArgument
}
myWallet, err := models.GetWalletByPassportIdentity(auth.ID, auth.Identity)
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
// Try to enrich bank meta if exists locally; ignore not found.
var cardInfo models.WalletBank
_ = impl.DBService.Where("card_no = ?", in.CardNo).First(&cardInfo).Error
data := &models.WalletBank{
WalletIdentity: myWallet.Identity,
CardNo: in.CardNo,
BankName: cardInfo.BankName,
Bank: cardInfo.Bank,
BankType: cardInfo.BankType,
CardOwner: in.CardOwner,
IDCard: in.IdCard,
Phone: in.Phone,
}
data.Identity = utils.UUID()
data.PassportID = auth.ID
data.PassportIdentity = auth.Identity
err = impl.DBService.Create(&data).Error
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
return &pb.StatusReply{
Message: vars.OK,
Timeseq: time.Now().UnixMilli(),
}, nil
}

View File

@@ -0,0 +1,60 @@
package basic
import (
"context"
"time"
"git.apinb.com/bsm-finance/wallet/internal/excode"
"git.apinb.com/bsm-finance/wallet/internal/impl"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/utils"
"git.apinb.com/bsm-sdk/core/vars"
)
// 申请提现
func ApplyCash(ctx context.Context, in *pb.ApplyCashRequest) (reply *pb.StatusReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
if in.Amount == 0 || in.Channel == 0 {
return nil, errcode.ErrInvalidArgument
}
myWallet, err := models.GetWalletByPassportIdentity(auth.ID, auth.Identity)
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
if in.Amount > myWallet.Balance {
return nil, excode.ErrBalanceNotEnough
}
data := &models.WalletApplyCash{
WalletIdentity: myWallet.Identity,
Amount: in.Amount,
Channel: int8(in.Channel),
Remark: in.Remark,
}
data.Identity = utils.UUID()
data.PassportID = auth.ID
data.PassportIdentity = auth.Identity
err = impl.DBService.Create(&data).Error
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
//生成提现单
return &pb.StatusReply{
Message: vars.OK,
Timeseq: time.Now().UnixMilli(),
}, nil
}

View File

@@ -0,0 +1,46 @@
package basic
import (
"context"
"time"
"git.apinb.com/bsm-finance/wallet/internal/impl"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/vars"
)
// 绑定微信或支付宝账户
func BindPaymentId(ctx context.Context, in *pb.BindPaymentIDRequest) (reply *pb.StatusReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
if in.PayType == 0 || in.AuthCode == "" {
return nil, errcode.ErrInvalidArgument
}
var filed string
//通过code获取微信或者支付宝的openid
if in.PayType == 1 {
filed = "wxpay_id"
}
if in.PayType == 2 {
filed = "alipay_id"
}
err = impl.DBService.Model(&models.WalletBasic{}).Where("passport_identity=?", auth.Identity).Update(filed, in.AuthCode).Error
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
return &pb.StatusReply{
Message: vars.OK,
Timeseq: time.Now().UnixMilli(),
}, nil
}

View File

@@ -0,0 +1,54 @@
package basic
import (
"context"
"git.apinb.com/bsm-finance/wallet/internal/impl"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/vars"
)
// 获取用户银行卡列表
func GetBankCard(ctx context.Context, in *pb.FinanceEmpty) (reply *pb.GetBankCardReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
myWallet, err := models.GetWalletByPassportIdentity(auth.ID, auth.Identity)
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
result := make([]*models.WalletBank, 0)
err = impl.DBService.Where("wallet_identity=?", myWallet.Identity).Find(&result).Error
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
data := make([]*pb.BankCardInfo, 0)
for _, card := range result {
data = append(data, &pb.BankCardInfo{
Id: int64(card.ID),
Bank: card.Bank,
BankName: card.BankName,
BankType: card.BankType,
BankNumber: card.CardNo,
BindId: card.BindID,
Phone: card.Phone,
CardOwner: card.CardOwner,
IdCard: card.IDCard,
Created: card.CreatedAt.Format(vars.YYYY_MM_DD),
})
}
return &pb.GetBankCardReply{
Data: data,
}, nil
}

View File

@@ -0,0 +1,86 @@
package basic
import (
"context"
"strconv"
"time"
"git.apinb.com/bsm-finance/wallet/internal/impl"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/vars"
)
// 获取钱包信息
func GetWallet(ctx context.Context, in *pb.GetWalletRequest) (reply *pb.GetWalletReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
myWallet, err := models.GetWalletByPassportIdentity(auth.ID, auth.Identity)
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
var transIn int8 = 1 // 收入
var transOut int8 = -1 // 支出
ymd, _ := strconv.Atoi(time.Now().Format(vars.YYYYMMDD))
ym := time.Now().Year()*100 + int(time.Now().Month())
var totalMoney int64
total := make(map[string]int64)
// 统计当日收入
if in.IsTotalTodayIn {
impl.DBService.Model(&models.WalletRecord{}).Select("sum(money)").Where("passport_id=? AND trans_type=? AND ymd=?", auth.ID, transIn, ymd).Scan(&totalMoney)
total["TodayIn"] = totalMoney
}
// 统计当日支出
if in.IsTotalTodayOut {
impl.DBService.Model(&models.WalletRecord{}).Select("sum(money)").Where("passport_id=? AND trans_type=? AND ymd=?", auth.ID, transOut, ymd).Scan(&totalMoney)
total["TodayOut"] = totalMoney
}
// 统计本月收入
if in.IsTotalMonthIn {
impl.DBService.Model(&models.WalletRecord{}).Select("sum(money)").Where("passport_id=? AND trans_type=? AND ym=?", auth.ID, transIn, ym).Scan(&totalMoney)
total["MonthIn"] = totalMoney
}
// 统计本月支出
if in.IsTotalMonthOut {
impl.DBService.Model(&models.WalletRecord{}).Select("sum(money)").Where("passport_id=? AND trans_type=? AND ym=?", auth.ID, transOut, ym).Scan(&totalMoney)
total["MonthOut"] = totalMoney
}
// 统计全部收入
if in.IsTotalAllIn {
impl.DBService.Model(&models.WalletRecord{}).Select("sum(money)").Where("passport_id=? AND trans_type=? ", auth.ID, transIn).Scan(&totalMoney)
total["AllIn"] = totalMoney
}
// 统计全部支出
if in.IsTotalAllOut {
impl.DBService.Model(&models.WalletRecord{}).Select("sum(money)").Where("passport_id=? AND trans_type=? ", auth.ID, transOut).Scan(&totalMoney)
total["AllOut"] = totalMoney
}
// remove useless fmt.Sprint side-effect-free call
return &pb.GetWalletReply{
PassportIdentity: myWallet.PassportIdentity,
WalletIdentity: myWallet.Identity,
Balance: myWallet.Balance,
WithdrawalBalance: myWallet.WithdrawalBalance,
Total: total,
Status: int32(myWallet.Status),
AlipayId: myWallet.AlipayID,
WxpayId: myWallet.WxpayID,
Created: myWallet.CreatedAt.Format(vars.YYYY_MM_DD_HH_MM_SS),
}, nil
}

View File

@@ -0,0 +1,37 @@
package basic
import (
"context"
"time"
"git.apinb.com/bsm-finance/wallet/internal/impl"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/vars"
)
// 删除银行卡
func RmBankCard(ctx context.Context, in *pb.RmBankCardRequest) (reply *pb.StatusReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
if in.Identity == "" {
return nil, errcode.ErrInvalidArgument
}
err = impl.DBService.Where("passport_identity=? and identity=?", auth.Identity, in.Identity).Delete(&models.WalletBank{}).Error
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
return &pb.StatusReply{
Message: vars.OK,
Timeseq: time.Now().UnixMilli(),
}, nil
}

View File

@@ -0,0 +1,39 @@
package basic
import (
"context"
"time"
"git.apinb.com/bsm-finance/wallet/internal/impl"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/vars"
)
// 设置支付密码
func SetPayPassword(ctx context.Context, in *pb.SetPayPasswordRequest) (reply *pb.StatusReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
if in.Password == "" {
return nil, errcode.ErrPassword
}
encPassword := EncodePassword(in.Password, auth.Identity)
err = impl.DBService.Model(&models.WalletBasic{}).Where("passport_identity", auth.Identity).Update("pay_password", encPassword).Error
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
return &pb.StatusReply{
Message: vars.OK,
Timeseq: time.Now().UnixMilli(),
}, nil
}

View File

@@ -0,0 +1,54 @@
package basic
import (
"context"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/vars"
)
// 查询交易记录
func Transactions(ctx context.Context, in *pb.TransactionsRequest) (reply *pb.TransactionsReply, err error) {
auth, err := service.ParseMetaCtx(ctx, nil)
if err != nil {
return nil, err
}
if in.Page <= 1 {
in.Page = 1
}
if in.PageSize <= 1 {
in.PageSize = 20
}
list, total, err := models.FindWalletRecords(auth.Identity, in.Start, in.End, in.TransType, in.TradeType, in.Page, in.PageSize)
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
res := &pb.TransactionsReply{
Total: total,
}
data := make([]*pb.Transaction, 0)
if len(list) > 0 {
for _, item := range list {
data = append(data, &pb.Transaction{
TransType: int32(item.TransType),
TradeType: int32(item.TradeType),
InTradeNo: item.InTradeNo,
OutTradeNo: item.OutTradeNo,
Money: item.Money,
Fee: item.Fee,
PayChannel: int32(item.PayChannel),
PayType: item.PayType,
Remark: item.Remark,
Created: item.CreatedAt.Format(vars.YYYY_MM_DD_HH_MM_SS),
})
}
}
res.Records = data
return res, nil
}

View File

@@ -0,0 +1,69 @@
// Package basic 钱包基础业务逻辑
package basic
import (
"context"
"git.apinb.com/bsm-finance/wallet/internal/config"
"git.apinb.com/bsm-finance/wallet/internal/excode"
"git.apinb.com/bsm-finance/wallet/internal/impl"
"git.apinb.com/bsm-finance/wallet/internal/models"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/utils"
)
// WalletPay 钱包支付结构体
type WalletPay struct {
ctx context.Context
Body *models.WalletBasic
}
// NewWallet 创建钱包支付实例
// 验证用户身份、钱包状态、支付密码和余额
func NewWallet(passportIdentity, pwd string, amount int64) (*WalletPay, error) {
// 检查钱包是否存在
wallet := models.WalletExists(passportIdentity)
if wallet == nil {
return nil, errcode.ErrNotFound(404, "wallet")
}
// 检查钱包状态
if wallet.Status == -1 {
return nil, errcode.ErrPermissionDenied
}
// 验证支付密码
encPassword := EncodePassword(pwd, passportIdentity)
if encPassword != wallet.PayPassword {
return nil, errcode.ErrPassword
}
// 检查余额是否足够
if amount > wallet.Balance {
return nil, excode.ErrBalanceNotEnough
}
return &WalletPay{
Body: wallet,
}, nil
}
// TradeConsum 执行交易消费
// 扣除钱包余额和可提现余额
func (srv *WalletPay) TradeConsum(amount int64) error {
data := map[string]interface{}{
"balance": srv.Body.Balance - amount,
"withdrawal_balance": srv.Body.WithdrawalBalance - amount,
}
err := impl.DBService.Model(&models.WalletBasic{}).Where("identity=?", srv.Body.Identity).UpdateColumns(data).Error
if err != nil {
return err
}
return nil
}
// EncodePassword 加密支付密码
// 使用SHA256算法对密码进行加密
func EncodePassword(pwd, passportIdentity string) string {
return utils.Sha256(pwd, passportIdentity+config.Spec.Wallet.PublicKey)
}

View File

@@ -0,0 +1,138 @@
package payment
import (
"context"
"strings"
"git.apinb.com/bsm-finance/wallet/internal/excode"
"git.apinb.com/bsm-finance/wallet/internal/logic/alipay"
"git.apinb.com/bsm-finance/wallet/internal/logic/wechat"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/env"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/utils"
)
// 支付-充值
func ByCharge(ctx context.Context, in *pb.ChargeRequest) (reply *pb.PaymentReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
if in.Amount == 0 || in.Desc == "" {
return nil, errcode.ErrInvalidArgument
}
var (
myWallet, wErr = models.GetWalletByPassportIdentity(auth.ID, auth.Identity)
orderNo = utils.RandomString(32)
paymentRecrod = models.WalletPayment{
WalletIdentity: myWallet.Identity,
Type: 1, //1充值2为订单
OrderNo: orderNo,
PayChannel: int8(in.PayChannel),
PayType: in.PayType,
Amount: in.Amount,
}
)
if wErr != nil || myWallet.Identity == "" {
return nil, errcode.ErrNotFound(404, "wallet")
}
paymentRecrod.Identity = utils.UUID()
paymentRecrod.PassportID = auth.ID
paymentRecrod.PassportIdentity = auth.Identity
var payBodyAttach strings.Builder
payBodyAttach.WriteString(env.Runtime.Workspace)
payBodyAttach.WriteString(",")
payBodyAttach.WriteString(paymentRecrod.Identity)
replyResult := map[string]string{}
switch in.PayChannel {
case 1:
if in.OpenId == "" {
return nil, errcode.ErrInvalidArgument
}
wechat, err := wechat.NewWechat()
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrWechatInit
}
res, err := wechat.GetResponse(payBodyAttach.String(), orderNo, in.Desc, in.OpenId, in.Amount)
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrWechatGetResp
}
switch in.PayType {
case "JSAPI":
replyResult, err = wechat.JsapiPaySign(res.PrepayId)
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrWechatGetResp
}
case "APP":
replyResult, err = wechat.AppPaySign(res.PrepayId)
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrWechatGetResp
}
case "MINI":
replyResult, err = wechat.MiniPaySign(res.PrepayId)
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrWechatGetResp
}
default:
return nil, excode.ErrPayType
}
case 2:
var result string
alipay, err := alipay.NewAlipay()
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrAlipayInit
}
alipay.SetBody(payBodyAttach.String(), orderNo, in.Amount)
switch in.PayType {
case "WAP":
result, err = alipay.TradeWapPay()
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrAlipayGetResp
}
case "APP":
result, err = alipay.TradeAppPay()
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrAlipayGetResp
}
default:
return nil, excode.ErrPayType
}
replyResult["payUrl"] = result
default:
return nil, excode.ErrPayChannel
}
//写入支付记录表(不可忽略错误)
if err := models.CreatePaymentRecord(&paymentRecrod); err != nil {
printer.Error("CreatePaymentRecord failed: ", err.Error())
return nil, errcode.ErrDB
}
return &pb.PaymentReply{
Code: 200,
Result: replyResult,
}, nil
}

View File

@@ -0,0 +1,162 @@
package payment
import (
"context"
"strings"
"git.apinb.com/bsm-finance/wallet/internal/excode"
"git.apinb.com/bsm-finance/wallet/internal/logic/alipay"
"git.apinb.com/bsm-finance/wallet/internal/logic/basic"
"git.apinb.com/bsm-finance/wallet/internal/logic/wechat"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/env"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/utils"
)
// 支付-电商订单
func ByOrder(ctx context.Context, in *pb.OrderRequest) (reply *pb.PaymentReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
if in.OrderNo == "" || in.Desc == "" {
return nil, errcode.ErrInvalidArgument
}
amount, err := models.GetTransPriceByOrderNo(in.OrderNo)
if err != nil {
return nil, errcode.ErrNotFound(404, "order")
}
var (
myWallet, wErr = models.GetWalletByPassportIdentity(auth.ID, auth.Identity)
paymentRecrod = models.WalletPayment{
WalletIdentity: myWallet.Identity,
Type: 2, //1充值2为订单
OrderNo: in.OrderNo,
PayChannel: int8(in.PayChannel),
PayType: in.PayType,
Amount: amount,
}
)
if wErr != nil || myWallet.Identity == "" {
return nil, errcode.ErrNotFound(404, "wallet")
}
paymentRecrod.Identity = utils.UUID()
paymentRecrod.PassportID = auth.ID
paymentRecrod.PassportIdentity = auth.Identity
var payBodyAttach strings.Builder
payBodyAttach.WriteString(env.Runtime.Workspace)
payBodyAttach.WriteString(",")
payBodyAttach.WriteString(paymentRecrod.Identity)
replyResult := make(map[string]string)
switch in.PayChannel {
case 1:
wechat, err := wechat.NewWechat()
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrWechatInit
}
res, err := wechat.GetResponse(payBodyAttach.String(), in.OrderNo, in.Desc, in.OpenId, amount)
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrWechatGetResp
}
switch in.PayType {
case "JSAPI":
replyResult, err = wechat.JsapiPaySign(res.PrepayId)
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrWechatGetResp
}
case "APP":
replyResult, err = wechat.AppPaySign(res.PrepayId)
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrWechatGetResp
}
case "MINI":
replyResult, err = wechat.MiniPaySign(res.PrepayId)
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrWechatGetResp
}
case "NATIVE":
replyResult, err = wechat.NativePaySign(payBodyAttach.String(), in.OrderNo, in.Desc, "", in.OpenId, amount)
if err == nil {
if v, ok := replyResult["code_url"]; ok {
replyResult["qr_code"] = v
}
} else {
printer.Error(err.Error())
return nil, excode.ErrWechatGetResp
}
default:
return nil, excode.ErrPayType
}
replyResult["prepay_id"] = res.PrepayId
case 2:
var result string
alipay, err := alipay.NewAlipay()
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrAlipayInit
}
alipay.SetBody(payBodyAttach.String(), in.OrderNo, amount)
switch in.PayType {
case "WAP":
result, err = alipay.TradeWapPay()
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrAlipayGetResp
}
case "APP":
result, err = alipay.TradeAppPay()
if err != nil {
printer.Error(err.Error())
return nil, excode.ErrAlipayGetResp
}
default:
return nil, excode.ErrPayType
}
replyResult["payUrl"] = result
case 3:
walletSrv, err := basic.NewWallet(auth.Identity, in.Password, amount)
if err != nil {
return nil, errcode.ErrDB
}
err = walletSrv.TradeConsum(amount)
if err != nil {
return nil, excode.ErrBalanceNotEnough
}
default:
return nil, excode.ErrPayChannel
}
// 写入支付记录表(不可忽略错误)
if err := models.CreatePaymentRecord(&paymentRecrod); err != nil {
printer.Error("CreatePaymentRecord failed: ", err.Error())
return nil, errcode.ErrDB
}
//return
return &pb.PaymentReply{
Code: 200,
Result: replyResult,
}, nil
}

View File

@@ -0,0 +1,40 @@
package payment
import (
"context"
"time"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/vars"
)
// 回调更新支付的结果和状态
func Callback(ctx context.Context, in *pb.CallbackRequest) (reply *pb.StatusReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
if in.Identity == "" {
return nil, errcode.ErrInvalidArgument
}
var status int8 = 0
if in.CallbackStatus {
status = 2
} else {
status = -1
}
err = models.UpsetWalletPaymentByIdentity(auth.Identity, in.Identity, status, in.CallbackMsg)
if err != nil {
return nil, err
}
return &pb.StatusReply{
Message: vars.OK,
Timeseq: time.Now().UnixMilli(),
}, nil
}

View File

@@ -0,0 +1,37 @@
package payment
import (
"context"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/service"
)
// 获取支付的详情
func Get(ctx context.Context, in *pb.IdentRequest) (reply *pb.PaymentItem, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
if in.Identity == "" {
return nil, errcode.ErrInvalidArgument
}
paymentRecord, err := models.GetWalletPaymentByIdentity(auth.Identity, in.Identity)
if err != nil {
return nil, errcode.ErrNotFound(404, "payment")
}
return &pb.PaymentItem{
OrderNo: paymentRecord.OrderNo,
TradeNo: paymentRecord.TradeNo,
Type: int32(paymentRecord.Type),
PayChannel: int32(paymentRecord.PayChannel),
PayType: paymentRecord.PayType,
Amount: paymentRecord.Amount,
Args: paymentRecord.Args,
Status: int32(paymentRecord.Status),
}, nil
}

View File

@@ -0,0 +1,27 @@
// Package payment 支付相关业务逻辑
package payment
import (
"context"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/service"
)
// Hello 支付服务健康检查接口
// 用于验证支付服务是否正常运行
func Hello(ctx context.Context, in *pb.Empty) (reply *pb.PaymentReply, err error) {
// 解析授权元数据
_, err = service.ParseMetaCtx(ctx, nil)
if err != nil {
return nil, err
}
// 返回服务状态信息
return &pb.PaymentReply{
Result: map[string]string{
"message": "Payment service is running",
"status": "healthy",
},
}, nil
}

View File

@@ -0,0 +1,48 @@
package payment
import (
"context"
"fmt"
"git.apinb.com/bsm-finance/wallet/internal/config"
"git.apinb.com/bsm-finance/wallet/internal/models"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/service"
)
// 获取平台支持的支付网关
func Way(ctx context.Context, in *pb.WayRequest) (reply *pb.WayReply, err error) {
auth, ok := service.ParseMetaCtx(ctx, nil)
if ok != nil {
return nil, ok
}
items := make([]*pb.WayItem, 0)
if config.Spec.Wallet.AlipyIsOpen {
items = append(items, &pb.WayItem{
Ident: "ALIPAY",
Title: "支付宝",
})
}
if config.Spec.Wallet.WechatpayIsOpen {
items = append(items, &pb.WayItem{
Ident: "WECHATPAY",
Title: "微信支付",
})
}
if config.Spec.Wallet.WalletIsOpen {
balance := fmt.Sprintf("%.2f", models.GetWalletBalanceByPassportIdentity(auth.Identity))
items = append(items, &pb.WayItem{
Ident: "WALLET",
Title: "钱包余额",
Intro: "余额:" + balance + " 元",
})
}
return &pb.WayReply{
Way: items,
}, nil
}

View File

@@ -0,0 +1,23 @@
package wechat
import (
"context"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/service"
)
// 微信APP支付下单
func AppPreOrder(ctx context.Context, in *pb.WxpayAppPreOrderRequest) (reply *pb.WxpayAppPreOrderReply, err error) {
// parse authorization meta.
_, err = service.ParseMetaCtx(ctx, nil)
if err != nil {
return nil, err
}
// TODO: valid code
// TODO: add your logic code & delete this line.
return
}

View File

@@ -0,0 +1,28 @@
package wechat
import (
"context"
pb "git.apinb.com/bsm-finance/wallet/pb"
)
// 微信JSAPI下单
func JsapiPreOrder(ctx context.Context, in *pb.WxpayJSAPIPreOrderRequest) (reply *pb.WxpayJSAPIPreOrderReply, err error) {
if err := CheckParam(in.Amount, in.AuthCode, in.UserIdentification, in.OrderNo); err != nil {
return nil, err
}
var (
attach = in.AuthCode // 授权码
orderNo = in.OrderNo // 订单唯一码
desc = in.Description // 商品描述
openid = in.UserIdentification // 用户微信唯一标识
amount = in.Amount // 订单金额
)
wx, _ := NewWechat()
res, err := wx.GetResponse(attach, orderNo, desc, openid, amount)
if err != nil {
return nil, err
}
return &pb.WxpayJSAPIPreOrderReply{PrepayId: res.PrepayId}, nil
}

View File

@@ -0,0 +1,23 @@
package wechat
import (
"context"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/service"
)
// 微信native二维码支付下单
func NativePreOrder(ctx context.Context, in *pb.WxpayNativePreOrderRequest) (reply *pb.WxpayNativePreOrderReply, err error) {
// parse authorization meta.
_, err = service.ParseMetaCtx(ctx, nil)
if err != nil {
return nil, err
}
// TODO: valid code
// TODO: add your logic code & delete this line.
return
}

View File

@@ -0,0 +1,23 @@
package wechat
import (
"context"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/service"
)
// 微信转账到零钱
func Transfer(ctx context.Context, in *pb.WxpayTransferRequest) (reply *pb.WxpayTransferReply, err error) {
// parse authorization meta.
_, err = service.ParseMetaCtx(ctx, nil)
if err != nil {
return nil, err
}
// TODO: valid code
// TODO: add your logic code & delete this line.
return
}

View File

@@ -0,0 +1,196 @@
package wechat
import (
"context"
"encoding/json"
"os"
"time"
"git.apinb.com/bsm-finance/wallet/internal/config"
"git.apinb.com/bsm-finance/wallet/internal/excode"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/wechat/v3"
)
type WeChatPay struct {
ctx context.Context
Client *wechat.ClientV3
Body *gopay.BodyMap
}
var (
mchID string
SerialNumber string
mchAPIv3Key string
ApiClientKey string
AppId string
Currency string
NotifyUrl string
)
func new() {
mchID = config.Spec.WeChat.MchID // 商户号
SerialNumber = config.Spec.WeChat.SerialNo // 商户证书序列号
mchAPIv3Key = config.Spec.WeChat.APIV3Key // 商户APIv3密钥
ApiClientKey = config.Spec.WeChat.PrivateKey // 私钥
AppId = config.Spec.WeChat.AppID // Appid
Currency = config.Spec.WeChat.Currency // 货币类型 CNY人民币境内商户号仅支持人民币。
NotifyUrl = config.Spec.WeChat.NotifyURL // 通知URL必须为直接可访问的URL不允许携带查询串要求必须为https地址。
}
func NewWechat() (*WeChatPay, error) {
new()
// NewClientV3 初始化微信客户端 v3
// mchid商户ID 或者服务商模式的 sp_mchid
// serialNo商户证书的证书序列号
// apiV3KeyapiV3Key商户平台获取
// privateKey私钥 apiclient_key.pem 读取后的内容
privateKey, err := os.ReadFile(ApiClientKey)
if err != nil {
return nil, err
}
client, err := wechat.NewClientV3(mchID, SerialNumber, mchAPIv3Key, string(privateKey))
if err != nil {
return nil, err
}
// 设置微信平台API证书和序列号如开启自动验签请忽略此步骤
//client.SetPlatformCert([]byte(""), "")
// 启用自动同步返回验签并定时更新微信平台API证书开启自动验签时无需单独设置微信平台API证书和序列号
err = client.AutoVerifySign()
if err != nil {
return nil, err
}
// 打开Debug开关输出日志默认是关闭的
client.DebugSwitch = gopay.DebugOn
return &WeChatPay{
ctx: context.Background(),
Client: client,
}, nil
}
// GetResponse tradeNo:finance_payment表的identity
func (srv *WeChatPay) GetResponse(Attach, OrderNo, desc, openid string, amount int64) (*wechat.Prepay, error) {
expire := time.Now().Add(60 * time.Minute).Format(time.RFC3339)
// 初始化 BodyMap
bm := make(gopay.BodyMap)
bm.Set("appid", AppId).
Set("mchid", mchID).
Set("description", desc).
Set("attach", Attach). //finance_payment表的identity 附加数据在查询API和支付通知中原样返回可作为自定义参数使用实际情况下只有支付完成状态才会返回该字段
Set("out_trade_no", OrderNo).
Set("time_expire", expire).
Set("notify_url", NotifyUrl).
SetBodyMap("amount", func(bm gopay.BodyMap) {
bm.Set("total", amount).
Set("currency", Currency)
}).
SetBodyMap("payer", func(bm gopay.BodyMap) {
bm.Set("openid", openid)
})
wxRsp, err := srv.Client.V3TransactionJsapi(srv.ctx, bm)
if err != nil {
return nil, err
}
return wxRsp.Response, nil
}
func (srv *WeChatPay) MiniPaySign(prepayid string) (map[string]string, error) {
applet, err := srv.Client.PaySignOfApplet(AppId, prepayid)
if err != nil {
return nil, err
}
reply, err := struct2map(applet)
if err != nil {
return nil, err
}
return reply, nil
}
func (srv *WeChatPay) AppPaySign(prepayid string) (map[string]string, error) {
app, err := srv.Client.PaySignOfApp(AppId, prepayid)
if err != nil {
return nil, err
}
reply, err := struct2map(app)
if err != nil {
return nil, err
}
return reply, nil
}
func (srv *WeChatPay) JsapiPaySign(prepayid string) (map[string]string, error) {
jsapi, err := srv.Client.PaySignOfJSAPI(AppId, prepayid)
if err != nil {
return nil, err
}
reply, err := struct2map(jsapi)
if err != nil {
return nil, err
}
return reply, nil
}
func (srv *WeChatPay) NativePaySign(Attach, OrderNo, description, expire, openid string, amount int64) (map[string]string, error) {
// 初始化 BodyMap
body := make(gopay.BodyMap)
body.Set("appid", AppId).
Set("mchid", mchID).
Set("description", description).
Set("attach", Attach). //finance_payment表的identity 附加数据在查询API和支付通知中原样返回可作为自定义参数使用实际情况下只有支付完成状态才会返回该字段
Set("out_trade_no", OrderNo).
Set("time_expire", expire).
Set("notify_url", NotifyUrl).
SetBodyMap("amount", func(bm gopay.BodyMap) {
bm.Set("total", amount).
Set("currency", Currency)
}).
SetBodyMap("payer", func(bm gopay.BodyMap) {
bm.Set("openid", openid)
})
native, err := srv.Client.V3TransactionNative(srv.ctx, body)
if err != nil {
return nil, err
}
reply, err := struct2map(native)
if err != nil {
return nil, err
}
return reply, nil
}
func struct2map(in any) (map[string]string, error) {
b, err := json.Marshal(in)
var m map[string]string
err = json.Unmarshal(b, &m)
return m, err
}
func CheckParam(amount int64, auth_code string, identity string, order_no string) (err error) {
if amount == 0 {
return excode.ErrAmount
}
if auth_code == "" {
return excode.ErrAuthCode
}
if identity == "" {
return excode.ErrPassportId
}
if order_no == "" {
return excode.ErrOrderNo
}
return nil
}

View File

@@ -0,0 +1,41 @@
package wechat
import (
"context"
"encoding/json"
"git.apinb.com/bsm-finance/wallet/internal/config"
pb "git.apinb.com/bsm-finance/wallet/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"github.com/go-pay/gopay/wechat/v3"
)
// 微信支付回调
func WxCallback(ctx context.Context, in *pb.WxCallBackRequest) (reply *pb.CallBackReply, err error) {
var notifyReq = &wechat.V3NotifyReq{}
data, err := json.Marshal(in)
if err != nil {
return nil, errcode.ErrJsonMarshal
}
err = json.Unmarshal(data, &notifyReq)
if err != nil {
return nil, errcode.ErrJsonUnmarshal
}
wx, _ := NewWechat()
pubKey := wx.Client.WxPublicKeyMap()
err = notifyReq.VerifySignByPKMap(pubKey)
if err != nil {
printer.Error(err.Error())
return &pb.CallBackReply{Code: "FAIL", Message: "验签失败"}, nil
}
res, err := notifyReq.DecryptPayCipherText(config.Spec.WeChat.APIV3Key)
if err != nil {
printer.Error(err.Error())
return &pb.CallBackReply{Code: "FAIL", Message: "验签失败"}, err
}
// Todo: 业务逻辑
payResult, _ := json.Marshal(res)
return &pb.CallBackReply{Code: "SUCCESS", Message: string(payResult)}, nil
}