支付宝沙箱对接
- [1.1 官网](#1.1 官网)
- [1.2 秘钥生成(系统默认)](#1.2 秘钥生成(系统默认))
- [1.3 秘钥生成(软件生成)](#1.3 秘钥生成(软件生成))
- [1.4 golan 安装 SDK](#1.4 golan 安装 SDK)
- [1.5 GoLand 代码](#1.5 GoLand 代码)
-
- [1.6 前端代码](#1.6 前端代码)
1.1 官网
沙箱官网:
https://open.alipay.com/develop/sandbox/app
秘钥用具下载:
https://opendocs.alipay.com/common/02kipk?pathHash=0d20b438
1.2 秘钥生成(系统默认)
1.3 秘钥生成(软件生成)
- 点击生成密钥
- 生成成功
- 自定义密钥
****
- 复制粘贴之前生成的公钥并点击保存
- 继续点击确认
1.4 golan 安装 SDK
go get -u github.com/smartwalle/alipay/v3
1.5 GoLand 代码
- app
- utils
- abfPay.go
- utils
go
package utils
import (
"fmt"
"github.com/smartwalle/alipay/v3"
"net/url"
)
func ZfbPay(orderID string, totalPrice string) string {
appID := "9021000131612134" // 你的appID
privateKey := "" // 你的私钥
aliPublicKey := "" // 支付宝的公钥
var client, err = alipay.New(appID, privateKey, false)
if err != nil {
panic(err)
}
err = client.LoadAliPayPublicKey(aliPublicKey)
if err != nil {
panic(err)
}
//var p = alipay.TradeWapPay{}
var p = alipay.TradePagePay{}
p.NotifyURL = "http://192.168.137.188:5173/#/pages/pay-success/pay-success" //支付宝回调
p.ReturnURL = "http://192.168.137.188:5173/#/pages/pay-success/pay-success" //支付后调转页面
p.Subject = "云尚校园-订单支付" //标题
p.OutTradeNo = orderID //传递一个唯一单号
p.TotalAmount = totalPrice //金额
//p.ProductCode = "QUICK_WAP_WAY"
p.ProductCode = "FAST_INSTANT_TRADE_PAY" //网页支付
var url2 *url.URL
url2, err = client.TradePagePay(p)
if err != nil {
fmt.Println(err)
}
var payURL = url2.String()
println(payURL)
return payURL
}
- app
- dto
- Pay.go
- dto
go
package dto
type ShopPay struct {
ByCode string `json:"byCode"`
ShopID string `json:"id"`
OrderTips string `json:"tips"`
OrderTotalPrice string `json:"totalPrice"`
OrderStatus string `json:"status"`
OrderID string `json:"order_id"`
}
- app
- model
- Pay.go
- model
go
package models
import "gorm.io/gorm"
//
// ShopPay
// @Description: 生成订单号
//
type ShopPay struct {
gorm.Model
ByCode string `gorm:"type:varchar(100)"`
OrderId string `gorm:"type:varchar(100); unique;not null"` // 订单ID
OrderStatus string `gorm:"type:varchar(100); not null"` // 订单状态
OrderTips string `gorm:"type:varchar(200); not null"` // 订单备注
OrderTotalPrice string `gorm:"type:varchar(100); not null"`
ShopID string `gorm:"type:varchar(100); not null"`
}
- app
- common
- databaseMySQL.go
- common
go
package common
import (
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
var DB *gorm.DB
func InitDB() *gorm.DB {
host := viper.GetString("datasource.host")
port := viper.GetString("datasource.port")
database := viper.GetString("datasource.database")
username := viper.GetString("datasource.username")
password := viper.GetString("datasource.password")
charset := viper.GetString("datasource.charset")
db, err := gorm.Open(mysql.Open(username+`:`+password+`@tcp(`+host+`:`+port+`)/`+database+`?charset=`+charset+`&parseTime=true&loc=Local`),
&gorm.Config{})
if err != nil {
panic("failed to connect database, err: " + err.Error())
}
db.AutoMigrate(&model.ShopPay{})
DB = db
return db
}
func GetDB() *gorm.DB {
return DB
}
- app
- config
- application.yml
- config
yaml
server:
port: 9999
datasource:
diverName: mysql
host: 127.0.0.1
port: 3306
database: go-app
username: root
password: 123456
charset: utf8
- app
- controller
- PayController.go
- controller
go
package controller
import "github.com/gin-gonic/gin"
type PayController interface {
AddShopPay(ctx *gin.Context)
OrderPay(ctx *gin.Context)
}
- app
- controller
- pay
- pay.go
- pay
- controller
go
package pay
import (
"github.com/gin-gonic/gin"
"go-app/common"
"go-app/controller"
"go-app/dto"
model "go-app/models"
"go-app/response"
"go-app/utils"
"gorm.io/gorm"
"strconv"
)
type PayFun interface {
controller.PayController
}
type payDB struct {
DB *gorm.DB
}
func PayFunction() PayFun {
db := common.GetDB()
db.AutoMigrate(model.User{})
return payDB{DB: db}
}
func (db payDB) AddShopPay(ctx *gin.Context) {
getPayData := dto.ShopPay{}
ctx.BindJSON(&getPayData)
getPayData.OrderStatus = "2"
getPayData.OrderID = strconv.FormatInt(utils.GetSnowflakeId(), 10)
if getPayData.ByCode != "" {
db.DB.Debug().Create(&getPayData)
}
response.Success(ctx, gin.H{"data": getPayData}, "success")
}
func (db payDB) OrderPay(ctx *gin.Context) {
order := model.ShopPay{}
id, _ := strconv.Atoi(ctx.Params.ByName("orderID"))
db.DB.Debug().Where("order_id", id).First(&order)
pay := utils.ZfbPay(order.OrderId, order.OrderTotalPrice)
response.Success(ctx, gin.H{"data": pay}, "success")
}
- app
- router.go
go
package main
import (
"github.com/gin-gonic/gin"
shopController "go-app/controller/shop"
)
func CollectRoute(r *gin.Engine) *gin.Engine {
// 支付页面
payGroup := r.Group("api/pay/")
payFun := payController.PayFunction()
payGroup.POST("/AddShopPay/", payFun.AddShopPay)
payGroup.POST("/orderPay/:orderID", payFun.OrderPay)
return r
}
- app
- main.go
go
package main
import (
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/spf13/viper"
"go-app/common"
"os"
)
func main() {
InitConfig()
common.InitDB()
r := gin.Default()
config := cors.DefaultConfig()
config.AllowAllOrigins = true //允许所有域名
config.AllowMethods = []string{"GET", "POST", "OPTIONS"} //允许请求的方法
config.AllowHeaders = []string{"token", "tus-resumable", "upload-length", "upload-metadata", "cache-control", "x-requested-with", "*"}
r.Use(cors.New(config))
// 定义路由和处理函数
r = CollectRoute(r)
port := viper.GetString("server.port")
if port != "" {
panic(r.Run(":" + port))
}
r.Run()
}
func InitConfig() {
workDir, _ := os.Getwd()
viper.SetConfigName("application")
viper.SetConfigType("yml")
viper.AddConfigPath(workDir + "/config")
err := viper.ReadInConfig()
if err != nil {
panic(err)
}
}
1.6 前端代码
html
<template>
<view>
<!-- 自定义导航栏 -->
<view class="box-bg" style="font-size: 36rpx;">
<!-- <uni-nav-bar shadow left-icon="left" right-icon="cart" title="购物车" /> -->
<uni-nav-bar shadow fixed="true" left-icon="left" right-text="关闭" title="支付订单" statusBar="true"
@clickLeft="backCommitShop" @clickRight="colsePay" />
</view>
<!-- 支付选择模块 -->
<view class="pay-main">
<radio-group name="">
<label>
<view class="pay-item">
<view v-for="(item,index) in payItemIamges.data" :key="index">
<view class="pay-connect">
<img class="pay-item-image" :src="item.imageUrl" alt="">
<view class="pay-item-text">
<view class="pay-item-text-top">{{item.nameFather}}</view>
<view class="pay-item-text-foot">{{item.name}}</view>
</view>
<label class="pay-radio">
<radio :checked="isChecked" color="#F33" /><text></text>
</label>
</view>
</view>
</view>
</label>
</radio-group>
</view>
<!-- 底部去支付模块 -->
<view class="foot-pay">
<view class="total-pay">
<view class="total">合计:</view>
<view class="total">¥{{payMoney}}</view>
</view>
<view class="go-pay" @tap="goPay">去支付</view>
</view>
</view>
</template>
<script setup>
import {
onLoad,
} from '@dcloudio/uni-app';
import {
reactive,
ref
} from "vue"
import {orderPay} from "@/api/shop/pay.js"
onLoad((e) => {
// 获取价格
payMoney.value = e.price;
// 获取订单号
orderID.value = e.orderID;
})
// 选择支付方式
const isChecked = ref(false);
const payItemIamges = reactive({
data: [{
nameFather: "微信支付",
name: "推荐微信用户使用",
imageUrl: "http://s1jh1gxy3.hn-bkt.clouddn.com/shopCartCommit/wPay.png"
},
{
nameFather: "支付宝支付",
name: "推荐支付宝用户使用",
imageUrl: "http://s1jh1gxy3.hn-bkt.clouddn.com/shopCartCommit/zPay.png"
}
]
})
// 获取金额
const payMoney = ref(0);
// 订单ID
const orderID = ref(0);
// 去支付
const goPay = () => {
uni.navigateTo({
url:"@/pages/pay-success/pay-success"
})
orderPay(orderID.value).then(res=>{
//#ifdef APP-PLUS
plus.runtime.openURL(res.data.data, function(res){
console.log(res);
})
//#endif
// #ifdef H5
window.open(res.data.data)
// #endif
})
}
const backCommitShop = () => {
uni.navigateBack({
delta: 1
})
}
const colsePay = () => {
uni.navigateTo({
url: "../shop-commit/shop-commit"
})
}
</script>
<style lang="less" scoped>
// 底部样式
.foot-pay {
border-top: 2rpx solid #fcc;
line-height: 100rpx;
height: 100rpx;
width: 100%;
position: fixed;
bottom: 0;
left: 0;
display: flex;
justify-content: space-between;
align-items: center;
.total-pay {
display: flex;
flex: 1;
background-color: black;
color: white;
padding-left: 120rpx;
.total {
padding: 0rpx 7rpx;
}
}
.go-pay {
padding: 0rpx 100rpx;
color: white;
background-color: #49BDFB;
}
}
// 支付样式
.pay-main {
margin-top: 15rpx;
.pay-item {
.pay-connect {
display: flex;
justify-content: space-between;
padding: 20rpx 30rpx;
border-bottom: 8rpx solid #F5F5F5;
.pay-item-image {
width: 100rpx;
height: 100rpx;
}
.pay-item-text {
flex: 1;
padding-left: 80rpx;
.pay-item-text-top {
font-weight: bold;
}
.pay-item-text-foot {
color: #636263;
}
}
.pay-radio {
padding-top: 20rpx;
}
}
}
}
.box-bg {
background-color: #F5F5F5;
padding: 0 5px 0;
}
::v-deep uni-text.uni-nav-bar-text.uni-ellipsis-1 {
font-size: 34rpx;
font-weight: bolder;
}
::v-deep uni-text.uni-nav-bar-right-text {
font-size: 32rpx;
font-weight: bolder;
}
pay-item-text {
flex: 1;
padding-left: 80rpx;
.pay-item-text-top {
font-weight: bold;
}
.pay-item-text-foot {
color: #636263;
}
}
.pay-radio {
padding-top: 20rpx;
}
}
}
}
.box-bg {
background-color: #F5F5F5;
padding: 0 5px 0;
}
::v-deep uni-text.uni-nav-bar-text.uni-ellipsis-1 {
font-size: 34rpx;
font-weight: bolder;
}
::v-deep uni-text.uni-nav-bar-right-text {
font-size: 32rpx;
font-weight: bolder;
}
</style>