250 lines
5.8 KiB
Go
250 lines
5.8 KiB
Go
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),
|
||
// 微信为发券方商户分配的公众账号ID,接口传入的所有appid应该为公众号的appid(在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的)。
|
||
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.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.Body != nil {
|
||
return 0, c.bodyErr(ctx, result)
|
||
}
|
||
|
||
return 0, err
|
||
}
|
||
|
||
return CpnStatus(*resp.Status).GetStatus()
|
||
}
|
||
|
||
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.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.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.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
|
||
}
|