mirror of
https://gitee.com/zoujingli/ThinkAdmin.git
synced 2025-04-06 03:58:04 +08:00
增加支付通道配置
This commit is contained in:
parent
478229e5a1
commit
6aa47275bc
18
admin_v6.sql
18
admin_v6.sql
@ -104,8 +104,23 @@ CREATE TABLE `data_news_x_comment` (
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '数据-文章-评论' ROW_FORMAT = COMPACT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of data_news_x_comment
|
||||
-- Table structure for data_payment
|
||||
-- ----------------------------
|
||||
DROP TABLE IF EXISTS `data_payment`;
|
||||
CREATE TABLE `data_payment` (
|
||||
`id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '支付类型',
|
||||
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '支付名称',
|
||||
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '支付参数',
|
||||
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '支付说明',
|
||||
`sort` bigint(20) UNSIGNED NULL DEFAULT 0 COMMENT '排序权重',
|
||||
`status` tinyint(1) UNSIGNED NULL DEFAULT 1 COMMENT '支付状态(1使用,0禁用)',
|
||||
`deleted` tinyint(1) UNSIGNED NULL DEFAULT 0 COMMENT '删除状态',
|
||||
`create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `idx_data_news_mark_status`(`status`) USING BTREE,
|
||||
INDEX `idx_data_news_mark_deleted`(`deleted`) USING BTREE
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '数据-支付-通道' ROW_FORMAT = COMPACT;
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for data_user
|
||||
@ -4506,6 +4521,7 @@ INSERT INTO `system_menu` VALUES (83, 73, '配送区域管理', 'layui-icon layu
|
||||
INSERT INTO `system_menu` VALUES (84, 68, '微信小程序配置', 'layui-icon layui-icon-set', 'data/config/wxapp', 'data/config/wxapp', '', '_self', 0, 1, '2020-09-21 16:34:08');
|
||||
INSERT INTO `system_menu` VALUES (85, 68, '会员服务协议', 'layui-icon layui-icon-template-1', 'data/config/agreement', 'data/config/agreement', '', '_self', 30, 1, '2020-09-22 16:00:10');
|
||||
INSERT INTO `system_menu` VALUES (86, 68, '关于我们描述', 'layui-icon layui-icon-app', 'data/config/about', 'data/config/about', '', '_self', 40, 1, '2020-09-22 16:12:44');
|
||||
INSERT INTO `system_menu` VALUES (87, 68, '商城支付配置', 'layui-icon layui-icon-set-sm', 'data/payment/index', 'data/payment/index', '', '_self', 0, 1, '2020-12-12 09:08:09');
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for system_oplog
|
||||
|
116
app/data/controller/Payment.php
Normal file
116
app/data/controller/Payment.php
Normal file
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace app\data\controller;
|
||||
|
||||
use app\data\service\PaymentService;
|
||||
use think\admin\Controller;
|
||||
|
||||
/**
|
||||
* 支付通道通道
|
||||
* Class Payment
|
||||
* @package app\data\controller
|
||||
*/
|
||||
class Payment extends Controller
|
||||
{
|
||||
/**
|
||||
* 绑定数据表
|
||||
* @var string
|
||||
*/
|
||||
private $table = 'DataPayment';
|
||||
|
||||
/**
|
||||
* 支付通道类型
|
||||
* @var array
|
||||
*/
|
||||
protected $types = PaymentService::TYPES;
|
||||
|
||||
/**
|
||||
* 支付通道管理
|
||||
* @auth true
|
||||
* @menu true
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->title = '支付通道管理';
|
||||
$query = $this->_query($this->table);
|
||||
$query->where(['deleted' => 0])->order('sort desc,id desc');
|
||||
$query->equal('type,status')->like('name')->dateBetween('create_at')->page();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加支付通道
|
||||
* @auth true
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$this->title = '添加支付通道';
|
||||
$this->_form($this->table, 'form');
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑支付通道
|
||||
* @auth true
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$this->title = '编辑支付通道';
|
||||
$this->_form($this->table, 'form');
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据表单处理
|
||||
* @param array $data
|
||||
*/
|
||||
protected function _form_filter(array &$data)
|
||||
{
|
||||
if ($this->request->isGet()) {
|
||||
$data['content'] = json_decode($data['content'] ?? '[]', true) ?: [];
|
||||
} else {
|
||||
$data['content'] = json_encode($this->request->post() ?: [], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 表单结果处理
|
||||
* @param boolean $state
|
||||
*/
|
||||
protected function _form_result(bool $state)
|
||||
{
|
||||
if ($state) {
|
||||
$this->success('支付通道保存成功!', 'javascript:history.back()');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改支付通道状态
|
||||
* @auth true
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function state()
|
||||
{
|
||||
$this->_save($this->table, $this->_vali([
|
||||
'status.in:0,1' => '状态值范围异常!',
|
||||
'status.require' => '状态值不能为空!',
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除支付通道
|
||||
* @auth true
|
||||
* @throws \think\db\exception\DbException
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$this->_delete($this->table);
|
||||
}
|
||||
|
||||
}
|
@ -23,4 +23,18 @@ class Data extends Controller
|
||||
$this->success('获取轮播图片数据', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付通道数据
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function getPayment()
|
||||
{
|
||||
$map = ['status' => 1, 'deleted' => 0];
|
||||
$query = $this->app->db->name('DataPayment')->where($map);
|
||||
$result = $query->order('sort desc,id desc')->field('id,name,type')->select();
|
||||
$this->success('获取支付通道数据', $result->toArray());
|
||||
}
|
||||
|
||||
}
|
@ -2,8 +2,8 @@
|
||||
|
||||
namespace app\data\controller\api;
|
||||
|
||||
use app\data\service\OrderService;
|
||||
use app\wechat\service\WechatService;
|
||||
use app\data\service\payment\JoinPayService;
|
||||
use app\data\service\payment\WechatPayService;
|
||||
use think\admin\Controller;
|
||||
|
||||
/**
|
||||
@ -14,7 +14,7 @@ use think\admin\Controller;
|
||||
class Notify extends Controller
|
||||
{
|
||||
/**
|
||||
* 微信支付通知处理
|
||||
* 微信支付通知
|
||||
* @param string $scene 支付场景
|
||||
* @return string
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
@ -24,46 +24,27 @@ class Notify extends Controller
|
||||
*/
|
||||
public function wxpay(string $scene = 'order'): string
|
||||
{
|
||||
$notify = ($payment = WechatService::WePayOrder())->getNotify();
|
||||
if ($notify['result_code'] == 'SUCCESS' && $notify['return_code'] == 'SUCCESS') {
|
||||
if ($scene === 'order') {
|
||||
if ($this->setOrder($notify['out_trade_no'], $notify['cash_fee'] / 100, $notify['transaction_id'], 'wxpay')) {
|
||||
return $payment->getNotifySuccessReply();
|
||||
}
|
||||
}
|
||||
// ... 其他支付场景
|
||||
if (strtolower($scene) === 'order') {
|
||||
return WechatPayService::instance()->notify();
|
||||
} else {
|
||||
return 'success';
|
||||
}
|
||||
return $payment->getNotifySuccessReply();
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单状态更新
|
||||
* @param string $code 订单单号
|
||||
* @param string $amount 交易金额
|
||||
* @param string $paycode 交易单号
|
||||
* @param string $paytype 支付类型
|
||||
* @return boolean
|
||||
* 汇聚支付通知
|
||||
* @param string $scene 支付场景
|
||||
* @return string
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
private function setOrder(string $code, string $amount, string $paycode, string $paytype = 'wxpay'): bool
|
||||
public function joinpay(string $scene = 'order'): string
|
||||
{
|
||||
// 检查订单支付状态
|
||||
$map = ['order_no' => $code, 'payment_status' => 0, 'status' => 2];
|
||||
$order = $this->app->db->name('StoreOrder')->where($map)->find();
|
||||
if (empty($order)) return false;
|
||||
// 更新订单支付状态
|
||||
$this->app->db->name('StoreOrder')->where($map)->update([
|
||||
'status' => 3,
|
||||
'payment_type' => $paytype,
|
||||
'payment_code' => $paycode,
|
||||
'payment_status' => 1,
|
||||
'payment_amount' => $amount,
|
||||
'payment_remark' => '微信在线支付',
|
||||
'payment_datetime' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
// 调用用户升级机制
|
||||
return OrderService::instance()->syncAmount($order['order_no']);
|
||||
if (strtolower($scene) === 'order') {
|
||||
return JoinPayService::instance()->notify();
|
||||
} else {
|
||||
return 'success';
|
||||
}
|
||||
}
|
||||
}
|
@ -5,8 +5,8 @@ namespace app\data\controller\api\auth;
|
||||
use app\data\controller\api\Auth;
|
||||
use app\data\service\GoodsService;
|
||||
use app\data\service\OrderService;
|
||||
use app\data\service\PaymentService;
|
||||
use app\data\service\TruckService;
|
||||
use app\wechat\service\WechatService;
|
||||
use think\admin\extend\CodeExtend;
|
||||
use think\exception\HttpResponseException;
|
||||
|
||||
@ -169,7 +169,7 @@ class Order extends Auth
|
||||
$update = ['status' => 2, 'amount_express' => $express['template_amount']];
|
||||
$update['amount_total'] = $order['amount_goods'] + $amount - $order['amount_reduct'] - $order['amount_discount'];
|
||||
if ($this->app->db->name('ShopOrder')->where($map)->update($update) !== false) {
|
||||
$this->success('订单确认成功!', $this->_getPaymentParams($order['order_no'], $order['amount_total']));
|
||||
$this->success('订单确认成功!', ['order_no' => $order['order_no']]);
|
||||
} else {
|
||||
$this->error('订单确认失败,请稍候再试!');
|
||||
}
|
||||
@ -183,13 +183,17 @@ class Order extends Auth
|
||||
*/
|
||||
public function payment()
|
||||
{
|
||||
$map = $this->_vali(['order_no.require' => '订单单号不能为空!']);
|
||||
$data = $this->_vali([
|
||||
'payid.require' => '支付通道不能为空!',
|
||||
'order_no.require' => '订单单号不能为空!',
|
||||
]);
|
||||
$map = ['order_no' => $data['order_no']];
|
||||
$order = $this->app->db->name('ShopOrder')->where($map)->find();
|
||||
if (empty($order)) $this->error('获取订单数据失败,请稍候再试!');
|
||||
if ($order['status'] != 2) $this->error('该订单不能发起支付哦!');
|
||||
if ($order['payment_status']) $this->error('订单已经支付,不需要再次支付哦!');
|
||||
try {
|
||||
$params = $this->_getPaymentParams($order['order_no'], $order['amount_total']);
|
||||
$params = PaymentService::build($data['payid'])->create($this->user['openid'], $order['order_no'], $order['amount_total'], '商城订单支付', '');
|
||||
$this->success('获取支付参数成功!', $params);
|
||||
} catch (HttpResponseException $exception) {
|
||||
throw $exception;
|
||||
@ -198,30 +202,6 @@ class Order extends Auth
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单支付参数
|
||||
* @param string $code 订单单号
|
||||
* @param string $amount 支付金额
|
||||
* @return array
|
||||
*/
|
||||
private function _getPaymentParams(string $code, string $amount): array
|
||||
{
|
||||
try {
|
||||
return WechatService::WePayOrder()->create([
|
||||
'body' => '商城订单支付',
|
||||
'openid' => $this->user['openid'],
|
||||
'out_trade_no' => $code,
|
||||
'total_fee' => $amount * 100,
|
||||
'trade_type' => 'JSAPI',
|
||||
'notify_url' => sysuri('@data/api.notify/wxpay/type/order', [], false, true),
|
||||
'spbill_create_ip' => $this->app->request->ip(),
|
||||
]);
|
||||
} catch (\Exception $exception) {
|
||||
$this->error("创建支付参数失败,{$exception->getMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 主动取消未支付的订单
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
|
139
app/data/service/PaymentService.php
Normal file
139
app/data/service/PaymentService.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace app\data\service;
|
||||
|
||||
use app\data\service\payment\JoinPayService;
|
||||
use app\data\service\payment\WechatPayService;
|
||||
use think\admin\Service;
|
||||
|
||||
/**
|
||||
* 支付基础服务
|
||||
* Class PaymentService
|
||||
* @package app\data\service
|
||||
*/
|
||||
abstract class PaymentService extends Service
|
||||
{
|
||||
// 微信支付类型
|
||||
const PAYMENT_WECHAT_GZH = 'wechat_gzh';
|
||||
const PAYMENT_WECHAT_XCX = 'wechat_xcx';
|
||||
|
||||
// 汇聚支付类型
|
||||
const PAYMENT_JOINPAY_GZH = 'joinpay_gzh';
|
||||
const PAYMENT_JOINPAY_XCX = 'joinpay_xcx';
|
||||
|
||||
// 支付通道描述
|
||||
const TYPES = [
|
||||
PaymentService::PAYMENT_WECHAT_XCX => '微信小程序支付',
|
||||
PaymentService::PAYMENT_WECHAT_GZH => '微信服务号支付',
|
||||
PaymentService::PAYMENT_JOINPAY_XCX => '汇聚小程序支付',
|
||||
PaymentService::PAYMENT_JOINPAY_GZH => '汇聚服务号支付',
|
||||
];
|
||||
|
||||
/**
|
||||
* 默认支付类型
|
||||
* @var string
|
||||
*/
|
||||
protected static $type;
|
||||
|
||||
/**
|
||||
* 当前支付通道
|
||||
* @var array
|
||||
*/
|
||||
protected static $config;
|
||||
|
||||
/**
|
||||
* 支付服务对象
|
||||
* @var JoinPayService|WechatPayService
|
||||
*/
|
||||
protected static $driver = [];
|
||||
|
||||
|
||||
/**
|
||||
* 根据配置实例支付服务
|
||||
* @param string $payid 支付通道编号
|
||||
* @return JoinPayService|WechatPayService
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public static function build(string $payid): PaymentService
|
||||
{
|
||||
if (isset(static::$driver[$payid])) {
|
||||
return static::$driver[$payid];
|
||||
}
|
||||
// 支付通道配置验证
|
||||
$map = ['id' => $payid, 'status' => 1, 'deleted' => 0];
|
||||
$payment = app()->db->name('DataPayment')->where($map)->find();
|
||||
if (empty($payment)) {
|
||||
throw new \think\Exception("支付通道[#{$payid}]已关闭");
|
||||
}
|
||||
static::$config = json_decode(static::$config['content'], true);
|
||||
if (empty(static::$config)) {
|
||||
throw new \think\Exception("支付通道[#{$payid}]配置无效");
|
||||
}
|
||||
// 支付通道类型验证
|
||||
if (empty(static::TYPES[$payment['type']])) {
|
||||
throw new \think\Exception("支付通道[{$payment['type']}]未定义");
|
||||
}
|
||||
// 实例化具体支付通道类型
|
||||
static::$type = $payment['type'];
|
||||
if (stripos(static::$type, 'wechat_') === 0) {
|
||||
return static::$driver[$payid] = WechatPayService::instance();
|
||||
} else {
|
||||
return static::$driver[$payid] = JoinPayService::instance();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单更新操作
|
||||
* @param string $code 订单单号
|
||||
* @param string $payno 交易单号
|
||||
* @param string $amount 支付金额
|
||||
* @param string $paytype 支付类型
|
||||
* @return bool
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function updateOrder(string $code, string $payno, string $amount, string $paytype): bool
|
||||
{
|
||||
// 检查订单支付状态
|
||||
$map = ['order_no' => $code, 'payment_status' => 0, 'status' => 2];
|
||||
$order = $this->app->db->name('StoreOrder')->where($map)->find();
|
||||
if (empty($order)) return false;
|
||||
// 更新订单支付状态
|
||||
$this->app->db->name('StoreOrder')->where($map)->update([
|
||||
'status' => 3,
|
||||
'payment_type' => $paytype,
|
||||
'payment_code' => $payno,
|
||||
'payment_status' => 1,
|
||||
'payment_amount' => $amount,
|
||||
'payment_remark' => '微信在线支付',
|
||||
'payment_datetime' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
// 调用用户升级机制
|
||||
return OrderService::instance()->syncAmount($order['order_no']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付通知处理
|
||||
* @return string
|
||||
*/
|
||||
abstract public function notify(): string;
|
||||
|
||||
/**
|
||||
* 订单主动查询
|
||||
* @param string $orderNo
|
||||
* @return array
|
||||
*/
|
||||
abstract public function query(string $orderNo): array;
|
||||
|
||||
/**
|
||||
* 创建支付订单
|
||||
* @param string $openid 会员OPENID
|
||||
* @param string $orderNo 交易订单单号
|
||||
* @param string $payAmount 交易订单金额(元)
|
||||
* @param string $payTitle 交易订单名称
|
||||
* @param string $payDescription 订单订单描述
|
||||
* @return array
|
||||
*/
|
||||
abstract public function create(string $openid, string $orderNo, string $payAmount, string $payTitle, string $payDescription): array;
|
||||
}
|
164
app/data/service/payment/JoinPayService.php
Normal file
164
app/data/service/payment/JoinPayService.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace app\data\service\payment;
|
||||
|
||||
use app\data\service\PaymentService;
|
||||
use think\admin\extend\HttpExtend;
|
||||
|
||||
/**
|
||||
* 汇聚支付基础服务
|
||||
* Class JoinPayService
|
||||
* @package app\store\service\payment
|
||||
*/
|
||||
class JoinPayService extends PaymentService
|
||||
{
|
||||
/**
|
||||
* 请求地址
|
||||
* @var string
|
||||
*/
|
||||
protected $uri;
|
||||
|
||||
/**
|
||||
* 应用编号
|
||||
* @var string
|
||||
*/
|
||||
protected $appid;
|
||||
|
||||
/**
|
||||
* 报备商户号
|
||||
* @var string
|
||||
*/
|
||||
protected $trade;
|
||||
|
||||
/**
|
||||
* 平台商户号
|
||||
* @var string
|
||||
*/
|
||||
protected $mchid;
|
||||
|
||||
/**
|
||||
* 平台商户密钥
|
||||
* @var string
|
||||
*/
|
||||
protected $mchkey;
|
||||
|
||||
/**
|
||||
* 汇聚支付服务初始化
|
||||
* @return JoinPayService
|
||||
*/
|
||||
protected function initialize(): JoinPayService
|
||||
{
|
||||
$this->appid = static::$config['joinpay_appid'];
|
||||
$this->trade = static::$config['joinpay_trade'];;
|
||||
$this->mchid = static::$config['joinpay_mch_id'];
|
||||
$this->mchkey = static::$config['joinpay_mch_key'];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建订单支付参数
|
||||
* @param string $openid 会员OPENID
|
||||
* @param string $orderNo 交易订单单号
|
||||
* @param string $payAmount 交易订单金额(元)
|
||||
* @param string $payTitle 交易订单名称
|
||||
* @param string $payDescription 订单订单描述
|
||||
* @return array
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function create(string $openid, string $orderNo, string $payAmount, string $payTitle, string $payDescription): array
|
||||
{
|
||||
$types = [
|
||||
static::PAYMENT_JOINPAY_GZH => 'WEIXIN_GZH',
|
||||
static::PAYMENT_JOINPAY_XCX => 'WEIXIN_XCX',
|
||||
];
|
||||
if (isset($types[static::$type])) {
|
||||
$type = $types[static::$type];
|
||||
} else {
|
||||
throw new \think\Exception('支付类型[' . static::$type . ']未配置定义!');
|
||||
}
|
||||
try {
|
||||
$data = [
|
||||
'p0_Version' => '1.0',
|
||||
'p1_MerchantNo' => $this->mchid,
|
||||
'p2_OrderNo' => $orderNo,
|
||||
'p3_Amount' => $payAmount * 100,
|
||||
'p4_Cur' => '1',
|
||||
'p5_ProductName' => $payTitle,
|
||||
'p6_ProductDesc' => $payDescription,
|
||||
'p9_NotifyUrl' => sysuri('@data/api.notify/joinpay/scene/order', [], false, true),
|
||||
'q1_FrpCode' => $type ?? '',
|
||||
'q5_OpenId' => $openid,
|
||||
'q7_AppId' => $this->appid,
|
||||
'qa_TradeMerchantNo' => $this->trade,
|
||||
];
|
||||
if (empty($data['q5_OpenId'])) unset($data['q5_OpenId']);
|
||||
$this->uri = 'https://www.joinpay.com/trade/uniPayApi.action';
|
||||
$result = $this->_doReuest($data);
|
||||
if (is_array($result) && isset($result['ra_Code']) && intval($result['ra_Code']) === 100) {
|
||||
return json_decode($result['rc_Result'], true);
|
||||
} elseif (is_array($result) && isset($result['rb_CodeMsg'])) {
|
||||
throw new \think\Exception($result['rb_CodeMsg']);
|
||||
} else {
|
||||
throw new \think\Exception('获取预支付码失败!');
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
throw new \think\Exception($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单数据
|
||||
* @param string $orderNo
|
||||
* @return array
|
||||
*/
|
||||
public function query(string $orderNo): array
|
||||
{
|
||||
$this->uri = 'https://www.joinpay.com/trade/queryOrder.action';
|
||||
return $this->_doReuest(['p1_MerchantNo' => $this->mchid, 'p2_OrderNo' => $orderNo]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付结果处理
|
||||
* @return string
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function notify(): string
|
||||
{
|
||||
$notify = $this->app->request->get();
|
||||
foreach ($notify as &$item) $item = urldecode($item);
|
||||
if (empty($notify['hmac']) || $notify['hmac'] !== $this->_doSign($notify)) {
|
||||
return 'error';
|
||||
}
|
||||
if (isset($notify['r6_Status']) && intval($notify['r6_Status']) === 100) {
|
||||
if ($this->updateOrder($notify['r2_OrderNo'], $notify['r9_BankTrxNo'], $notify['r3_Amount'], 'joinpay')) {
|
||||
return 'success';
|
||||
}
|
||||
}
|
||||
return 'error';
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求数据签名
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
private function _doSign(array $data): string
|
||||
{
|
||||
ksort($data);
|
||||
unset($data['hmac']);
|
||||
return md5(join('', $data) . $this->mchkey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行数据请求
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
private function _doReuest($data = []): array
|
||||
{
|
||||
$data['hmac'] = $this->_doSign($data);
|
||||
return json_decode(HttpExtend::post($this->uri, $data), true);
|
||||
}
|
||||
}
|
105
app/data/service/payment/WechatPayService.php
Normal file
105
app/data/service/payment/WechatPayService.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace app\data\service\payment;
|
||||
|
||||
use app\data\service\PaymentService;
|
||||
use WePay\Order;
|
||||
|
||||
/**
|
||||
* 微信官方公众号支持
|
||||
* Class WechatPayService
|
||||
* @package app\store\service\payment
|
||||
*/
|
||||
class WechatPayService extends PaymentService
|
||||
{
|
||||
/**
|
||||
* 微信对象对象
|
||||
* @var Order
|
||||
*/
|
||||
protected $payment;
|
||||
|
||||
/**
|
||||
* 微信支付服务初始化
|
||||
* @return WechatPayService
|
||||
*/
|
||||
protected function initialize(): WechatPayService
|
||||
{
|
||||
$this->payment = Order::instance([
|
||||
'appid' => static::$config['wechat_appid'],
|
||||
'mch_id' => static::$config['wechat_mch_id'],
|
||||
'mch_key' => static::$config['wechat_mch_key'],
|
||||
'cache_path' => $this->app->getRuntimePath() . 'wechat',
|
||||
]);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询微信支付订单
|
||||
* @param string $orderNo
|
||||
* @return array
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \WeChat\Exceptions\LocalCacheException
|
||||
*/
|
||||
public function query(string $orderNo): array
|
||||
{
|
||||
return $this->payment->query(['out_trade_no' => $orderNo]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建微信支付订单
|
||||
* @param string $openid 会员OPENID
|
||||
* @param string $orderNo 交易订单单号
|
||||
* @param string $payAmount 交易订单金额(元)
|
||||
* @param string $payTitle 交易订单名称
|
||||
* @param string $payDescription 订单订单描述
|
||||
* @return array
|
||||
* @throws \think\Exception
|
||||
*/
|
||||
public function create(string $openid, string $orderNo, string $payAmount, string $payTitle, string $payDescription): array
|
||||
{
|
||||
try {
|
||||
$body = empty($payDescription) ? $payTitle : ($payTitle . '-' . $payDescription);
|
||||
$data = [
|
||||
'body' => $body,
|
||||
'openid' => $openid,
|
||||
'out_trade_no' => $orderNo,
|
||||
'total_fee' => $payAmount * 100,
|
||||
'trade_type' => 'JSAPI',
|
||||
'notify_url' => sysuri('@data/api.notify/wxpay/scene/order', [], false, true),
|
||||
'spbill_create_ip' => $this->app->request->ip(),
|
||||
];
|
||||
if (empty($data['openid'])) unset($data['openid']);
|
||||
$info = $this->payment->create($data);
|
||||
if ($info['return_code'] === 'SUCCESS' && $info['result_code'] === 'SUCCESS') {
|
||||
return $this->payment->jsapiParams($info['prepay_id']);
|
||||
}
|
||||
if (isset($info['err_code_des'])) {
|
||||
throw new \think\Exception($info['err_code_des']);
|
||||
} else {
|
||||
throw new \think\Exception('获取预支付码失败!');
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
throw new \think\Exception($exception->getMessage(), $exception->getCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付结果处理
|
||||
* @return string
|
||||
* @throws \WeChat\Exceptions\InvalidResponseException
|
||||
* @throws \think\db\exception\DataNotFoundException
|
||||
* @throws \think\db\exception\DbException
|
||||
* @throws \think\db\exception\ModelNotFoundException
|
||||
*/
|
||||
public function notify(): string
|
||||
{
|
||||
$notify = $this->payment->getNotify();
|
||||
if ($notify['result_code'] == 'SUCCESS' && $notify['return_code'] == 'SUCCESS') {
|
||||
if ($this->updateOrder($notify['out_trade_no'], $notify['transaction_id'], $notify['cash_fee'] / 100, 'wechat')) {
|
||||
return $this->payment->getNotifySuccessReply();
|
||||
}
|
||||
} else {
|
||||
return $this->payment->getNotifySuccessReply();
|
||||
}
|
||||
}
|
||||
}
|
66
app/data/view/payment/form.html
Normal file
66
app/data/view/payment/form.html
Normal file
@ -0,0 +1,66 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name='content'}
|
||||
<form class="layui-form layui-card" action="{:request()->url()}" data-auto="true" method="post" autocomplete="off">
|
||||
<div class="layui-card-body padding-40">
|
||||
<label class="layui-form-item relative block">
|
||||
<span class="color-green font-w7">支付名称</span>
|
||||
<span class="color-desc margin-left-5">Payment Name</span>
|
||||
<input class="layui-input" required placeholder="请输入支付名称" name="name" value="{$vo.name|default=''}"/>
|
||||
<span class="help-block"><b>必填,</b>请填写分类名称(如:微信小程序支付),建议字符不要太长,一般4-6个汉字</span>
|
||||
</label>
|
||||
<div class="layui-form-item">
|
||||
<span class="color-green font-w7 label-required-prev">支付通道</span>
|
||||
<span class="color-desc margin-left-5">Payment Channel</span>
|
||||
<div class="layui-input">
|
||||
{empty name='vo.type'}{php}$vo['type'] = 'wechat_xcx';{/php}{/empty}
|
||||
{foreach $types as $k => $v}
|
||||
<label class="think-radio notselect">
|
||||
{if $vo.type eq $k}
|
||||
<input data-payment-type checked type="radio" name="type" value="{$k}" lay-ignore> {$v}
|
||||
{else}
|
||||
<input data-payment-type type="radio" name="type" value="{$k}" lay-ignore> {$v}
|
||||
{/if}
|
||||
</label>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-payment-type="wechat">{include file='payment/form_wechat'}</div>
|
||||
<div data-payment-type="joinpay" class="layui-hide">{include file='payment/form_joinpay'}</div>
|
||||
|
||||
<div class="layui-form-item relative block">
|
||||
<span class="color-green font-w7">支付描述</span>
|
||||
<span class="color-desc margin-left-5">Payment Remark</span>
|
||||
<label class="relative block">
|
||||
<textarea class="layui-textarea" placeholder="请输入支付描述" name="desc">{$vo.desc|default=''}</textarea>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="hr-line-dashed"></div>
|
||||
{notempty name='vo.id'}<input type='hidden' value='{$vo.id}' name='id'>{/notempty}
|
||||
|
||||
<div class="layui-form-item text-center">
|
||||
<button class="layui-btn" type='submit'>保存数据</button>
|
||||
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消编辑吗?" data-history-back>取消编辑</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{/block}
|
||||
|
||||
{block name='script'}
|
||||
<script>
|
||||
$('input[data-payment-type]').on('change', function () {
|
||||
var type = $('input[data-payment-type]:checked').val();
|
||||
if (type.indexOf('wechat') > -1) {
|
||||
$('[data-payment-type="wechat"]').removeClass('layui-hide')
|
||||
$('[data-payment-type="joinpay"]').addClass('layui-hide');
|
||||
} else {
|
||||
$('[data-payment-type="wechat"]').addClass('layui-hide')
|
||||
$('[data-payment-type="joinpay"]').removeClass('layui-hide');
|
||||
}
|
||||
}).trigger('change');
|
||||
</script>
|
||||
{/block}
|
23
app/data/view/payment/form_joinpay.html
Normal file
23
app/data/view/payment/form_joinpay.html
Normal file
@ -0,0 +1,23 @@
|
||||
<label class="layui-form-item margin-bottom-20 block relative">
|
||||
<span class="color-green font-s14 font-w7 margin-right-10">商户绑定的公众号</span>
|
||||
<input name="joinpay_appid" required placeholder="请输入商户绑定的公众号(必填)" value="{$vo.content.joinpay_appid|default=''}" class="layui-input">
|
||||
<span class="help-block">商户绑定的公众号,授权给汇聚支付平台的公众号APPID</span>
|
||||
</label>
|
||||
|
||||
<label class="layui-form-item margin-bottom-20 block relative">
|
||||
<span class="color-green font-s14 font-w7">汇聚支付报备商户号</span>
|
||||
<input name="joinpay_trade" required maxlength="15" placeholder="请输入汇聚支付报备商户号(必填)" value="{$vo.content.joinpay_trade|default=''}" class="layui-input">
|
||||
<span class="help-block">汇聚支付报备商户号,需要联系汇聚支付平台的客服获取,通常以 777 开头的15位数字!</span>
|
||||
</label>
|
||||
|
||||
<label class="layui-form-item margin-bottom-20 block relative">
|
||||
<span class="color-green font-s14 font-w7 margin-right-10">汇聚支付的商户编号</span>
|
||||
<input name="joinpay_mch_id" required maxlength="15" placeholder="请输入汇聚支付的商户编号(必填)" value="{$vo.content.joinpay_mch_id|default=''}" class="layui-input">
|
||||
<span class="help-block">汇聚支付的商户编号,需要在汇聚支付平台商户中心获取,通常是以 888 开头的15位数字!</span>
|
||||
</label>
|
||||
|
||||
<label class="layui-form-item margin-bottom-20 block relative">
|
||||
<span class="color-green font-s14 font-w7 margin-right-10">汇聚支付的商户密钥</span>
|
||||
<input name="joinpay_mch_key" required maxlength="32" placeholder="请输入汇聚支付的商户密钥(必填)" value="{$vo.content.joinpay_mch_key|default=''}" class="layui-input">
|
||||
<span class="help-block">汇聚支付的商户密钥,需要在汇聚支付平台商户中心的密钥管理处获取,通常为32位字符串!</span>
|
||||
</label>
|
29
app/data/view/payment/form_wechat.html
Normal file
29
app/data/view/payment/form_wechat.html
Normal file
@ -0,0 +1,29 @@
|
||||
<label class="layui-form-item margin-bottom-20 block relative">
|
||||
<span class="color-green font-s14 font-w7 margin-right-10">绑定公众号</span>
|
||||
<input name="wechat_appid" required placeholder="请输入绑定公众号(必填)" value="{$vo.content.wechat_appid|default=''}" class="layui-input">
|
||||
<span class="help-block">商户绑定的公众号,授权给汇聚支付平台的公众号APPID</span>
|
||||
</label>
|
||||
|
||||
<label class="layui-form-item margin-bottom-20 block relative">
|
||||
<span class="color-green font-s14 font-w7 margin-right-10">微信商户号</span>
|
||||
<input name="wechat_mch_id" required placeholder="请输入微信商户ID(必填)" value="{$vo.content.wechat_mch_id|default=''}" class="layui-input">
|
||||
<span class="help-block">微信商户编号,需要在微信商户平台获取,微信商户号 与 公众号APPID 匹配</span>
|
||||
</label>
|
||||
|
||||
<label class="layui-form-item margin-bottom-20 block relative">
|
||||
<span class="color-green font-s14 font-w7 margin-right-10">微信商户密钥</span>
|
||||
<input name="wechat_mch_key" required maxlength="32" placeholder="请输入微信商户密钥(必填)" value="{$vo.content.wechat_mch_key|default=''}" class="layui-input">
|
||||
<span class="help-block">微信商户密钥,需要在微信商户平台操作设置密码并获取密钥,建议定期更换密钥</span>
|
||||
</label>
|
||||
|
||||
<label class="layui-form-item margin-bottom-20 block relative">
|
||||
<span class="color-green font-s14 font-w7">微信商户密钥文件内容</span><span class="nowrap color-desc">( 需要填写文件的全部内容 )</span>
|
||||
<textarea name="wechat_mch_key_text" placeholder="请输入微信KEY密钥内容" class="layui-textarea">{$vo.content.wechat_mch_key_text|default=''}</textarea>
|
||||
<span class="help-block">从商户平台上下载支付证书,解压并取得其中的 apiclient_key.pem 用记事本打开并复制文件内容填至此处</span>
|
||||
</label>
|
||||
|
||||
<label class="layui-form-item margin-bottom-20 block relative">
|
||||
<span class="color-green font-s14 font-w7">微信商户证书文件内容</span><span class="nowrap color-desc">( 需要填写文件的全部内容 )</span>
|
||||
<textarea name="wechat_mch_cert_text" placeholder="请输入微信CERT证书内容" class="layui-textarea">{$vo.content.wechat_mch_cert_text|default=''}</textarea>
|
||||
<span class="help-block">从商户平台上下载支付证书,解压并取得其中的 apiclient_cert.pem 用记事本打开并复制文件内容填至此处</span>
|
||||
</label>
|
66
app/data/view/payment/index.html
Normal file
66
app/data/view/payment/index.html
Normal file
@ -0,0 +1,66 @@
|
||||
{extend name="../../admin/view/main"}
|
||||
|
||||
{block name="button"}
|
||||
<!--{if auth("add")}-->
|
||||
<button data-open='{:url("add")}' class='layui-btn layui-btn-sm layui-btn-primary'>添加支付</button>
|
||||
<!--{/if}-->
|
||||
<!--{if auth("remove")}-->
|
||||
<button data-action='{:url("remove")}' data-rule="id#{key}" data-confirm="确定要删除这些支付吗?" class='layui-btn layui-btn-sm layui-btn-primary'>删除支付</button>
|
||||
<!--{/if}-->
|
||||
{/block}
|
||||
|
||||
{block name='content'}
|
||||
<div class="think-box-shadow table-block">
|
||||
{include file='payment/index_search'}
|
||||
<table class="layui-table margin-top-10" lay-skin="line">
|
||||
{notempty name='list'}
|
||||
<thead>
|
||||
<tr>
|
||||
<th class='list-table-check-td think-checkbox'>
|
||||
<label><input data-auto-none data-check-target='.list-check-box' type='checkbox'/></label>
|
||||
</th>
|
||||
<th class='list-table-sort-td'>
|
||||
<button type="button" data-reload class="layui-btn layui-btn-xs">刷 新</button>
|
||||
</th>
|
||||
<th class="text-left nowrap">支付名称</th>
|
||||
<th class="text-left nowrap">支付通道</th>
|
||||
<th class="text-left nowrap">支付状态</th>
|
||||
<th class="text-left nowrap">创建时间</th>
|
||||
<th class="text-left nowrap"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{/notempty}
|
||||
<tbody>
|
||||
{foreach $list as $key=>$vo}
|
||||
<tr data-dbclick>
|
||||
<td class='list-table-check-td think-checkbox'>
|
||||
<label><input class="list-check-box" value='{$vo.id}' type='checkbox'/></label>
|
||||
</td>
|
||||
<td class='list-table-sort-td'>
|
||||
<label><input data-action-blur="{:request()->url()}" data-value="id#{$vo.id};action#sort;sort#{value}" data-loading="false" value="{$vo.sort}" class="list-sort-input"></label>
|
||||
</td>
|
||||
<td class="text-left nowrap">{$vo.name|default=''}</td>
|
||||
<td class="text-left nowrap">{$types[$vo.type]??$vo.type}</td>
|
||||
<td>{if $vo.status eq 0}<span class="color-red">已禁用</span>{elseif $vo.status eq 1}<span class="color-green">已激活</span>{/if}</td>
|
||||
<td class="text-left nowrap">{$vo.create_at|format_datetime}</td>
|
||||
<td class='text-left nowrap'>
|
||||
{if auth("edit")}
|
||||
<a data-dbclick class="layui-btn layui-btn-xs" data-open="{:url('edit')}?id={$vo.id}">编 辑</a>
|
||||
{/if}
|
||||
{if auth("state") and $vo.status eq 1}
|
||||
<a class="layui-btn layui-btn-xs layui-btn-warm" data-action="{:url('state')}" data-value="id#{$vo.id};status#0">禁 用</a>
|
||||
{/if}
|
||||
{if auth("state") and $vo.status eq 0}
|
||||
<a class="layui-btn layui-btn-xs layui-btn-warm" data-action="{:url('state')}" data-value="id#{$vo.id};status#1">激 活</a>
|
||||
{/if}
|
||||
{if auth("remove")}
|
||||
<a class="layui-btn layui-btn-xs layui-btn-danger" data-confirm="确定要删除该支付吗?" data-action="{:url('remove')}" data-value="id#{$vo.id}">删 除</a>
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</tbody>
|
||||
</table>
|
||||
{empty name='list'}<span class="notdata">没有记录哦</span>{else}{$pagehtml|raw|default=''}{/empty}
|
||||
</div>
|
||||
{/block}
|
49
app/data/view/payment/index_search.html
Normal file
49
app/data/view/payment/index_search.html
Normal file
@ -0,0 +1,49 @@
|
||||
<fieldset>
|
||||
<legend>条件搜索</legend>
|
||||
<form class="layui-form layui-form-pane form-search" action="{:request()->url()}" onsubmit="return false" method="get" autocomplete="off">
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">支付名称</label>
|
||||
<label class="layui-input-inline">
|
||||
<input name="name" value="{:input('name','')}" placeholder="请输入支付名称" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">支付通道</label>
|
||||
<div class="layui-input-inline">
|
||||
<select class="layui-select" name="type">
|
||||
<option value="">-- 全部 --</option>
|
||||
{foreach $types as $k=>$v}
|
||||
{if $k eq input('type')}
|
||||
<option selected value="{$k}">{$v}</option>
|
||||
{else}
|
||||
<option value="{$k}">{$v}</option>
|
||||
{/if}{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">使用状态</label>
|
||||
<div class="layui-input-inline">
|
||||
<select class="layui-select" name="status">
|
||||
{foreach [''=>'-- 全部 --','0'=>'已禁用的记录','1'=>'已激活的记录'] as $k=>$v}
|
||||
{if $k.'' eq input('status')}
|
||||
<option selected value="{$k}">{$v}</option>
|
||||
{else}
|
||||
<option value="{$k}">{$v}</option>
|
||||
{/if}{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-form-item layui-inline">
|
||||
<label class="layui-form-label">创建时间</label>
|
||||
<label class="layui-input-inline">
|
||||
<input data-date-range name="create_at" value="{:input('create_at','')}" placeholder="请选择创建时间" class="layui-input">
|
||||
</label>
|
||||
</div>
|
||||
<div class="layui-form-item layui-inline">
|
||||
<button class="layui-btn layui-btn-primary"><i class="layui-icon"></i> 搜 索</button>
|
||||
</div>
|
||||
</form>
|
||||
</fieldset>
|
||||
|
||||
<script>window.form.render()</script>
|
Loading…
x
Reference in New Issue
Block a user