voucher/internal/data/wechatrepoimpl/cpn.go

283 lines
6.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package wechatrepoimpl
import (
"context"
"encoding/json"
"fmt"
"github.com/go-kratos/kratos/v2/log"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons"
"io"
"net/http"
"time"
err2 "voucher/api/err"
"voucher/internal/biz/bo"
"voucher/internal/biz/vo"
"voucher/internal/biz/wechatrepo"
"voucher/internal/conf"
"voucher/internal/data"
"voucher/internal/pkg/request"
)
// CpnRepoImpl .
// @link https://pay.weixin.qq.com/doc/v3/merchant/4012463767
type CpnRepoImpl struct {
bc *conf.Bootstrap
Server data.Server
}
func NewCpnRepoImpl(bc *conf.Bootstrap) (wechatrepo.WechatCpnRepo, error) {
server := data.Server{
MchID: bc.Wechat.MchID,
MchCertificateSerialNumber: bc.Wechat.MchCertificateSerialNumber,
WechatPayPublicKeyID: bc.Wechat.WechatPayPublicKeyID,
}
if err := server.Validate(); err != nil {
return nil, err
}
return &CpnRepoImpl{bc: bc, Server: server}, nil
}
func (c *CpnRepoImpl) GetClient(ctx context.Context) (*core.Client, error) {
client, err := data.GetClient(ctx, c.Server)
if err != nil {
return nil, err2.ErrorWechatFAIL(err.Error())
}
return client, err
}
func (c *CpnRepoImpl) bodyErr(_ context.Context, result *core.APIResult) error {
bodyBytes, err := io.ReadAll(result.Response.Body)
if err != nil {
return err2.ErrorWechatFAIL(fmt.Sprintf("读取微信错误返回body报错:%s", err.Error()))
}
var errBody ErrBody
if err = json.Unmarshal(bodyBytes, &errBody); err != nil {
log.Errorf("微信错误返回body解析报错,body:%s,err:%s", string(bodyBytes), err.Error())
return err2.ErrorWechatFAIL(fmt.Sprintf("微信错误返回内容解析错误:%s", err.Error()))
}
return errBody.GetWechatError()
}
func (c *CpnRepoImpl) Order(ctx context.Context, order *bo.OrderBo) (string, error) {
req := cashcoupons.SendCouponRequest{
OutRequestNo: core.String(order.OrderNo),
Appid: core.String(order.AppID),
Openid: core.String(order.Account),
StockId: core.String(order.BatchNo),
StockCreatorMchid: core.String(order.MerchantNo),
}
client, err := c.GetClient(ctx)
if err != nil {
return "", err
}
svc := cashcoupons.CouponApiService{Client: client}
resp, result, err := svc.SendCoupon(ctx, req)
if err != nil {
if result.Response != nil && result.Response.Body != nil {
return "", c.bodyErr(ctx, result)
}
return "", err
}
return *resp.CouponId, nil
}
func (c *CpnRepoImpl) Query(ctx context.Context, orderWechat *bo.OrderBo) (vo.OrderStatus, error) {
req := cashcoupons.QueryCouponRequest{
CouponId: core.String(orderWechat.VoucherNo),
Appid: core.String(orderWechat.AppID),
Openid: core.String(orderWechat.Account),
}
client, err := c.GetClient(ctx)
if err != nil {
return 0, err
}
svc := cashcoupons.CouponApiService{Client: client}
resp, result, err := svc.QueryCoupon(ctx, req)
if err != nil {
if result.Response != nil && result.Response.Body != nil {
return 0, c.bodyErr(ctx, result)
}
return 0, err
}
cpnStatus := CpnStatus(*resp.Status)
if cpnStatus.IsRevoked() {
}
return CpnStatus(*resp.Status).GetStatus()
}
func (c *CpnRepoImpl) QueryCoupon(ctx context.Context, orderWechat *bo.OrderBo) (*cashcoupons.Coupon, error) {
req := cashcoupons.QueryCouponRequest{
CouponId: core.String(orderWechat.VoucherNo),
Appid: core.String(orderWechat.AppID),
Openid: core.String(orderWechat.Account),
}
client, err := c.GetClient(ctx)
if err != nil {
return nil, err
}
svc := cashcoupons.CouponApiService{Client: client}
resp, result, err := svc.QueryCoupon(ctx, req)
if err != nil {
if result.Response != nil && result.Response.Body != nil {
return nil, c.bodyErr(ctx, result)
}
return nil, err
}
return resp, nil
}
func (c *CpnRepoImpl) QueryProduct(ctx context.Context, stockCreatorMchId, stockId string) (*cashcoupons.Stock, error) {
if stockCreatorMchId == "" || stockId == "" {
return nil, err2.ErrorWechatFAIL("商户号或批次号不能为空")
}
client, err := c.GetClient(ctx)
if err != nil {
return nil, err
}
req := cashcoupons.QueryStockRequest{
StockId: core.String(stockId),
StockCreatorMchid: core.String(stockCreatorMchId),
}
svc := cashcoupons.StockApiService{Client: client}
response, result, err := svc.QueryStock(ctx, req)
if err != nil {
if result.Response != nil && result.Response.Body != nil {
return nil, c.bodyErr(ctx, result)
}
return nil, err
}
return response, nil
}
func (c *CpnRepoImpl) QueryCallback(ctx context.Context) (*cashcoupons.Callback, error) {
client, err := c.GetClient(ctx)
if err != nil {
return nil, err
}
svc := cashcoupons.CallBackUrlApiService{Client: client}
response, result, err := svc.QueryCallback(ctx, cashcoupons.QueryCallbackRequest{
Mchid: core.String(c.bc.Wechat.MchID),
})
if err != nil {
if result.Response != nil && result.Response.Body != nil {
return nil, c.bodyErr(ctx, result)
}
return nil, err
}
return response, nil
}
func (c *CpnRepoImpl) SetCallback(ctx context.Context, url string) (*cashcoupons.SetCallbackResponse, error) {
client, err := c.GetClient(ctx)
if err != nil {
return nil, err
}
svc := cashcoupons.CallBackUrlApiService{Client: client}
response, result, err := svc.SetCallback(ctx, cashcoupons.SetCallbackRequest{
Mchid: core.String(c.bc.Wechat.MchID),
NotifyUrl: core.String(url),
Switch: core.Bool(true),
})
if err != nil {
if result.Response != nil && result.Response.Body != nil {
return nil, c.bodyErr(ctx, result)
}
return nil, err
}
return response, nil
}
func (c *CpnRepoImpl) RegisterNotifyTag(ctx context.Context, stockID string) error {
// 注册“刚哥那边”回调中心tag,一个批次只能注册一次消费时根据不同的tag消费(区分测试/正式注册tag处理)
h := http.Header{
"Content-Type": []string{"application/json"},
}
b := struct {
TagName string `json:"tag_name"`
StockID string `json:"stock_id"`
}{
TagName: c.bc.WechatNotifyMQ.Tag,
StockID: stockID,
}
body, err := json.Marshal(b)
if err != nil {
return err
}
isSuccess := func(code int) bool {
return code == http.StatusOK
}
_, _, err = request.Post(
ctx,
c.bc.WechatNotifyMQ.RegisterTagUrl,
body,
request.WithHeaders(h),
request.WithStatusCodeFunc(isSuccess),
request.WithTimeout(time.Second*20),
)
if err != nil {
return err
}
return nil
}