支付宝支付(六):小程序支付(Go+Gin+内网穿透+支付回调处理)

一、前置条件

(1)go语言,1.18

(2)Gin、第三方依赖包:gopay【github.com/go-pay/gopay/alipay】https://github.com/go-pay/gopay/blob/main/doc/wechat_v3.md

(3)支付宝支付相关信息:appID ,支付宝用户私钥,支付宝公钥【小程序需要拉起支付宝APP,不能使用沙盒账号小程序文档 - 支付宝文档中心

(4)外网可访问的域名,用于支付成功回调;本地开发自测的话,搞个内网穿透工具;我用的花生壳,花了6块钱买了个域名【当时搞活动买1年,送98年】

(5)基于域名创建映射

(6)启动本地服务,验证映射是否成功 

 准备工作完成,开始编码!!

二、支付客户端代码:aliPayClient 

(1)保证单列,用go的sync.Once实现,具体代码如下

package lib

import (
	"github.com/go-pay/gopay/alipay"
	"log"
	. "go_online_pay/config"
	"sync"
)

var aliPayClient *alipay.Client

var syOnce sync.Once

//GetAliPayClient aliPayClient
func GetAliPayClient() *alipay.Client {
	var err error
	syOnce.Do(func() {
		cf := Conf{}
		config := cf.GetConf()
		aliPayClient, err = alipay.NewClient(config.AliPayAppId, config.AliPayPrivateKey, config.AliPayIsProd)
		if err != nil {
			log.Panic(err.Error())
		}
		//配置公共参数
		aliPayClient.SetCharset("utf-8").
			SetSignType(alipay.RSA2).
			SetNotifyUrl("")

	})
	return aliPayClient
}

(2)基础配置信息,都放到conf.yaml文件内了,同时配套conf.go文件,mian.go启动加载conf.ymal配置内容,后续通过conf.Xx获取属性值【config.AliPayAppId】,根据自己项目的实际情况,放置支付宝相关配置信息

#--------------支付宝支付测试账号(可真实支付)----------------
#是否生产账号
alipay_is_prod: true
#支付宝-APPID
alipay_app_id: 
#支付宝-用户私钥
alipay_private_key: 
#支付宝公钥
alipay_public_key:
#支付宝支付回调地址
alipay_notify_url: https://XXX/alipayNotify

三、支付服务类代码:AlipayService

(1)代码中含有:面对面(收款码链接),小程序支付,查询支付结果,退款,查询退款结果

package service

import (
	"context"
	"fmt"
	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/alipay"
	"go_online_pay/config"
	"go_online_pay/lib"
)

type AlipayService struct {
}

//TradePreCreate 当面付 扫码支付,获取二维码
func (AlipayService) TradePreCreate(bm gopay.BodyMap) string {
	client := lib.GetAliPayClient()
	client.SetNotifyUrl(config.GetConfLib().AliPayNotifyUrl)
	//统一收单线下交易预创建
	payParam, err := client.TradePrecreate(context.Background(), bm)
	if err != nil {
		fmt.Println(fmt.Sprintf("-----aliPay TradePreCreate()-------error:%s", err.Error()))
		return ""
	}
	return payParam.Response.QrCode
}

//TradeCreate 统一收单交易创建接口
func (AlipayService) TradeCreate(bm gopay.BodyMap) (*alipay.TradeCreateResponse, error) {
	client := lib.GetAliPayClient()
	//统一收单线下交易预创建
	payParam, err := client.TradeCreate(context.Background(), bm)
	if err != nil {
		fmt.Println(fmt.Sprintf("-----aliPay TradeCreate()-------error:%s", err.Error()))
		return nil, err
	}
	return payParam, nil
}

//Refund 退款接口
func (AlipayService) Refund(bm gopay.BodyMap) *alipay.TradeRefundResponse {
	client := lib.GetAliPayClient()
	//统一收单线下交易预创建
	payParam, err := client.TradeRefund(context.Background(), bm)
	if err != nil {
		fmt.Println(fmt.Sprintf("-----aliPay Refund()-------error:%s", err.Error()))
		return nil
	}
	//接口返回fund_change=Y为退款成功,fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态
	return payParam
}

//QueryRefund 查询退款接口
func (AlipayService) QueryRefund(orderNo, refundNo string) (*alipay.TradeFastpayRefundQueryResponse, error) {
	client := lib.GetAliPayClient()
	//统一收单线下交易预创建
	bm := make(gopay.BodyMap)
	//商户订单号
	bm.Set("out_trade_no", orderNo)
	//退款请求号
	bm.Set("out_request_no", refundNo)
	payParam, err := client.TradeFastPayRefundQuery(context.Background(), bm)
	if err != nil {
		fmt.Println(fmt.Sprintf("-----aliPay QueryRefund()-------error:%s", err.Error()))
	}
	//接口返回fund_change=Y为退款成功,fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态
	return payParam, err
}

//TradeQuery 交易查询接口
func (AlipayService) TradeQuery(orderNo string) *alipay.TradeQueryResponse {
	client := lib.GetAliPayClient()
	//请求参数
	bm := make(gopay.BodyMap)
	bm.Set("out_trade_no", orderNo)
	//统一收单线下交易预创建
	payParam, err := client.TradeQuery(context.Background(), bm)
	if err != nil {
		fmt.Println(fmt.Sprintf("-----aliPay TradeQuery(%s)-------error:%s", orderNo, err.Error()))
		return nil
	}
	return payParam
}

四、具体方法使用

(1)小程序支付部分代码

    //第三方30分钟过期
	expire := time.Now().Add(30 * 60 * time.Second).Format(time.RFC3339)
	description := util.ExtractStrByLen(orderInfo.CommodityName, 40)

		//请求参数
		bm := make(gopay.BodyMap)
		bm.Set("notify_url", config.GlobalConf.AliPayNotifyUrl)
		bm.Set("subject", description).
			//买家支付宝用户ID
			Set("buyer_id", openId).
			//商户订单号。由商家自定义
			Set("out_trade_no", payNo).
			//元,小数点两位,去掉千分位
			Set("total_amount", strings.Replace(payAmount, ",", "", -1))
		//统一收单线下交易预创建
		payParam, errAli := AlipayService{}.TradeCreate(bm)
		if errAli != nil {
			fmt.Println(fmt.Sprintf("-----aliPay TradeCreate() 返回值  ---errString -> %s", errAli.Error()))
			errMap := util.JsonToMap(errAli.Error())
			if val, ok := errMap["code"]; ok {
				result["errorMsg"] = "拉起支付宝支付失败:" + val.(string)
				return e.ERROR, result
			}
		}
		if payParam == nil || len(payParam.Response.TradeNo) == 0 {
			result["errorMsg"] = "拉起支付宝支付失败:无AliPrepayId"
			return e.ERROR, result
		}
		result["ali_prepay_Id"] = payParam.Response.TradeNo

前端拿到ali_prepay_Id后,通过sdk拉起支付宝APP

(2) 其他方法,都可以仿照小程序入参方式,根据支付宝接口文档填写相应的key即可

(3)支付回调方法代码:回调后写入队列,异步处理

//AlipayNotify 解析支付宝支付异步通知的参数到BodyMap
func AlipayNotify(c *gin.Context) {
	logger.Info("---------------------AlipayNotify START -------------")
	// c.Request 是 gin 框架的写法
	notifyReq, err := alipay.ParseNotifyToBodyMap(c.Request)
	if err != nil {
		logger.Info("------AlipayNotify ERR -------------,err:", err.Error())
		return
	}
	logger.Info("-----AlipayNotify notifyReq: ", notifyReq)

	ok, err := alipay.VerifySign(config.GlobalConf.AliPayPublicKey, notifyReq)
	if ok {
		if notifyReq["receipt_amount"] == nil {
			logger.Info("------ AlipayNotify 支付宝退款回调,忽略 -------------")
		} else {
			//验签成功
			var alReq = make(map[string]interface{})
			//商户订单号:商户系统内部订单号
			alReq["pay_no"] = notifyReq["out_trade_no"]
			alReq["trade_no"] = notifyReq["trade_no"]
			//与支付宝同步
			alReq["trade_status"] = notifyReq["trade_status"]
			alReq["notify_time"] = notifyReq["notify_time"]
			//订单金额。本次交易支付的订单金额,单位为人民币(元)
			totalAmountF, _ := strconv.ParseFloat(notifyReq["total_amount"].(string), 64)
			totalAmount := int(totalAmountF * 100)
			//实收金额。商家在交易中实际收到的款项,单位为人民币(元)。
			receiptAmountF, _ := strconv.ParseFloat(notifyReq["receipt_amount"].(string), 64)
			receiptAmount := int(receiptAmountF * 100)
			alReq["total_amount"] = totalAmount
			alReq["receipt_amount"] = receiptAmount

			var mapData = make(map[string]interface{})
			mapData["data_type"] = "PayNotify"
			mapData["param"] = map[string]interface{}{"payType": "aliPay", "notifyReq": alReq}
			reqJson, _ := json.Marshal(mapData)
			lib.PushMessToPayQueue(reqJson)
		}

	}

	/*	notifyReq["order_no"] = "订单号"
		notifyReq["trade_no"] = "2022061322001402060501532448"
		notifyReq["trade_status"] = "TRADE_SUCCESS"
		notifyReq["notify_time"] = "2022-06-13 10:09:36"
		notifyReq["total_amount"] = 202000
		notifyReq["receipt_amount"] = 200000
		var mapData = make(map[string]interface{})
		mapData["data_type"] = "PayNotify"
		mapData["param"] = map[string]interface{}{"payType": "aliPay", "notifyReq": notifyReq}
		reqJson, _ := json.Marshal(mapData)
		lib.PushMessToPayQueue(reqJson)*/

	// 此写法是 gin 框架返回支付宝的写法
	c.String(http.StatusOK, "%s", "success")
	logger.Info("---------------------AlipayNotify END -------------")
	return
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于golang gin微信点餐小程序的设计与实现需要考虑以下几个方面: 1. 架构设计:可以使用MVC(Model-View-Controller)架构,将代码按功能模块划分成不同的包,便于管理和维护。 2. 数据库设计:根据需求,设计适当的数据库结构,存储菜单信息、用户信息、订单信息等。可以使用关系型数据库如MySQL或非关系型数据库如MongoDB。 3. 用户认证与授权:使用微信小程序的登录功能进行用户认证,获取用户的OpenID等信息。为了保护用户数据安全,可以使用JWT(JSON Web Token)生成访问令牌,并设置权限控制,确保只有授权的用户能够访问相关接口。 4. 页面设计与交互:根据需求设计小程序的首页、菜单页、购物车、订单页等页面,合理安排页面布局和交互逻辑。可以使用小程序自带的模板或自定义组件进行页面开发。 5. 菜单管理:实现菜单的增删改查功能,包括菜品的图片、名称、价格、描述等信息。为了提高用户体验,可以增加搜索和排序等功能。 6. 购物车与订单管理:用户选择菜品后,将菜品添加到购物车,并生成相应的订单。可以实现购物车的增删改查功能,以及订单的支付、取消等功能。 7. 接口设计与实现:根据需求设计合适的API接口,包括菜单管理接口、用户认证接口、购物车接口、订单接口等。使用golanggin框架实现这些接口,并进行参数校验、错误处理等。 8. 部署与测试:将小程序部署到服务器上,并进行测试,确保功能正常运行。可以使用Postman等工具对API接口进行测试,模拟用户操作验证功能是否正确。 基于以上几个方面的设计与实现,可以打造一个基于golang gin的微信点餐小程序,实现用户点餐、下单和支付的功能,提供便捷的餐饮服务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值