前言

大家都知道支付宝支付和微信支付宝都只能局限在自己的平台,微信内支付宝支付是根本就不能使用,即使是公众号支付也需要跳转到外部浏览器才可以唤起支付宝支付,并且QQ浏览器唤起支付宝支付还是问题很多,所以一般在微信生态内的应用一般都不考虑接入支付宝,但仍然有不少用户有这方面的需求,今天就给大家做个详细接入流程!

开通支付宝支付

  1. 开通过程省略,可查看帮助文档:http://help.crmeb.net/crmeb_zsff/1514732,

    这是crmeb知识付费系统的支付宝支付,不过开通的流程都是一样的。

2. 下载支付宝支付SDK

3. 创建aliapay支付类

创建路径:crmeb/services/AlipayService.php,

把下载好的文件解压在vendor目录中目录结构为: /vendor/alipay/

4. AlipayService

采用单例设计模式,支付宝支付的SDK旧版本不能用composer加载这就很不优雅了,不能在类初始化的时候加载,而且第一次载入时非常慢。

<?php
/**
* @author: liaofei<136327134@qq.com>
* @day: 2020/8/19
*/ namespace crmeb\services; use think\exception\ValidateException;
use think\facade\Route as Url;
use think\facade\Log; /**
* Class AlipayService
* @package crmeb\services
*/
class AlipayService
{
/**
* @var static
*/
protected static $instance; /**
* @var string
*/
protected $alipayAppId; /**
* @var string
*/
protected $alipayPublicKey; /**
* 支付宝
* @var string
*/
protected $alipayPrivateKey; /**
* 同步回调地址
* @var string
*/
protected $returnUrl; /**
* 异步回调地址
* @var string
*/
protected $notifyUrl; /**
* 请求网关
* @var string
*/
protected $gatewayUrl = 'https://openapi.alipay.com/gateway.do'; /**
* 是否开启日志
* @var bool
*/
protected $isLog = false; /**
* AlipayService constructor.
*/
protected function __construct()
{
$this->initialize();
$this->options();
} /**
* @param $name
* @param $arguments
*/
public function __call($name, $arguments)
{
if (strstr($name, 'set') !== false) {
$name = ucwords(substr($name, 3, -1));
if (in_array($name, ['returnUrl', 'isLog', 'notifyUrl', 'alipayPrivateKey', 'alipayAppId', 'alipayPublicKey'])) {
$this->{$name} = $arguments[0];
}
} else {
throw new ValidateException('访问方法不存在');
}
} /**
* 初始化加载阿里云pay
*/
protected function initialize()
{
$dir = app()->getRuntimePath() . 'alipay';
if (!file_exists($dir)) {
mkdir($dir, 0775, true);
}
define('AOP_SDK_WORK_DIR', $dir);
include app()->getRootPath() . DS . 'vendor' . DS . 'alipay' . DS . 'AopSdk.php';
} /**
* 获取参数配置
*/
protected function options()
{
$this->alipayAppId = sys_config('alipay_app_id');
$this->alipayPublicKey = sys_config('alipay_public_key');
$this->alipayPrivateKey = sys_config('alipay_private_key');
$this->returnUrl = Url::buildUrl('/api/alipay/synchro')->domain(true)->build();
$this->notifyUrl = Url::buildUrl('/api/alipay/notify')->domain(true)->build();
} /**
* @return static
*/
public static function instance()
{
if (is_null(self::$instance)) {
self::$instance = new static();
}
return self::$instance;
} /**
* 支付宝支付同步回调
*/
public static function aliPayReturn()
{ } /**
* 支付支付同步回调
*/
public static function handleNotify()
{ } /**
* 下单支付手机网站支付版本
* @param string $outTradeNo 下单号
* @param string $totalAmount 订单金额 单位元
* @param string $subject 订单标题
* @param string $passbackParams 订单备注 会原样返回通常用于回调监听函数
* @param string $productCode 销售产品码,商家和支付宝签约的产品码
* @param bool $isView 是否直接输出
* @return $response 支付宝返回的信息
*/
public function aliPayWap(string $outTradeNo, string $totalAmount, string $subject, string $passbackParams, string $productCode = 'QUICK_MSECURITY_PAY', bool $isView = true)
{ } /**
* 统一收单交易退款接口
* @param string $outTradeNo 下单订单号
* @param string $tradeNo 支付宝订单号
* @param string $refundAmount 退款金额
* @param string $refundReason 退款说明
* @param string $passbackParams 备注
* @return $response 支付宝返回的信息
*/
public function aliPayRefund(string $outTradeNo, string $tradeNo, string $refundAmount, string $refundReason, string $passbackParams)
{ } /**
* 设置业务参数
* @param array $biz_content
* @return string
*/
protected function setBizContent(array $bizContent = [])
{
if (isset($bizContent['passback_params'])) $bizContent['passback_params'] = urlencode($bizContent['passback_params']);
if (isset($bizContent['trade_no']) && empty($bizContent['trade_no'])) unset($bizContent['trade_no']);
$bizContent = json_encode($bizContent);
//打印业务参数
$this->isLog && $this->writeLog($bizContent);
return $bizContent;
} /**
* 写入日志
* @param $content string | array | object
* @return Log
*/
protected function writeLog($content)
{
if (is_array($content)) $content = 'response: ' . var_export($content, true);
if (is_object($content)) $content = 'response: ' . var_export($content, true);
return Log::write(date('Y-m-d H:i:s', time()) . ' ' . $content);
} }

首先要把从数据库中获取到的参数放入支付配置里

创建aopclientRequestExecute()方法把 options()方法获取到的参数赋值给支付宝此方法会输出或者直接返回HTML的文本,前后端分离可直接用返回提交数据.

	/**
* 初始化参数
* @param $request
* @param bool $isView
* @return mixed|\SimpleXMLElement|string|\提交表单HTML文本
* @throws \Exception
*/
protected function aopclientRequestExecute(\AlipayTradeWapPayRequest $request, bool $isView = false)
{
$aop = new \AopClient();
//网管地址
$aop->gatewayUrl = $this->gatewayUrl;
//appid
$aop->appId = $this->alipayAppId;
//私钥
$aop->rsaPrivateKey = $this->alipayPrivateKey;
//公钥
$aop->alipayrsaPublicKey = $this->alipayPublicKey;
//版本
$aop->apiVersion = "1.0";
//编码格式
$aop->postCharset = 'UTF-8';
//内容格式
$aop->format = 'JSON';
//加密方式
$aop->signType = 'RSA2';
// 开启页面信息输出
$aop->debugInfo = false;
if ($isView) {
$result = $aop->pageExecute($request, "post");
echo $result;
} else {
$result = $aop->Execute($request);
}
//打开后,将报文写入log文件
$this->isLog && $this->writeLog($result);
return $result;
}

创建订单

上面已经创建好aliPayWap()方法,接下来我们来完成它,aliPayWap()方法的逻辑也非常的简单,只需要传入订单号,支付金额,订单标题,订单备注.订单备注一般会原样返回的,这样可以利用订单备注来让异步回调执行对应的方法

例如有用户下单支付和用户充值回调回调方法为一个,为一个回调方法处理的逻辑就比较混乱。

	/**
* 下单支付手机网站支付版本
* @param string $outTradeNo 下单号
* @param string $totalAmount 订单金额 单位元
* @param string $subject 订单标题
* @param string $passbackParams 订单备注 会原样返回通常用于回调监听函数
* @param string $productCode 销售产品码,商家和支付宝签约的产品码
* @param bool $isView 是否直接输出
* @return $response 支付宝返回的信息
*/
public function aliPayWap(string $outTradeNo, string $totalAmount, string $subject, string $passbackParams, string $productCode = 'QUICK_MSECURITY_PAY', bool $isView = true)
{
$request = new \AlipayTradeWapPayRequest();
//设置异步回调地址
$request->setNotifyUrl($this->notifyUrl);
//设置同步回调地址
$request->setReturnUrl($this->returnUrl);
//用内置方法格式化参数
$content = $this->setBizContent([
'out_trade_no' => $outTradeNo,
'total_amount' => $totalAmount,
'subject' => $subject,
'passback_params' => $passbackParams,
'product_code' => $productCode,
]);
//设置下单参数
$request->setBizContent($content);
//执行请求进行下单,返回对应的支付参数
return $this->aopclientRequestExecute($request, $isView);
}

订单退款

aliPayRefund()方法负责退款处理,需要参数下单订单号,支付宝订单号,退款金额,退款说明,备注.支付宝订单号需要在异步支付回调中或者同步回调中获取更新在数据库中,方便退款处理。

/**
* 统一收单交易退款接口
* @param string $outTradeNo 下单订单号
* @param string $tradeNo 支付宝订单号
* @param string $refundAmount 退款金额
* @param string $refundReason 退款说明
* @param string $passbackParams 备注
* @return $response 支付宝返回的信息
*/
public function aliPayRefund(string $outTradeNo, string $tradeNo, string $refundAmount, string $refundReason, string $passbackParams)
{
$request = new \AlipayTradeRefundRequest();
$content = $this->setBizContent([
'out_trade_no' => $outTradeNo,
'trade_no' => $tradeNo,
'refund_amount' => $refundAmount,
'passback_params' => $passbackParams,
'refund_reason' => $refundReason,
'product_code' => $passbackParams,
]);
$request->setBizContent($content);
return $this->aopclientRequestExecute($request);
}

回调验签

主要给异步和同步回调验证签名和数据处理方便调用

	/**
* 验签方法
* @param array $post 验签支付宝返回的信息,使用支付宝公钥。
* @return boolean
*/
protected function aliPaycheck(array $post)
{
$aop = new \AopClient();
$aop->alipayrsaPublicKey = $this->alipayPublicKey;
return $aop->rsaCheckV1($post, $this->alipayPrivateKey, 'RSA2');
}

异步回调

创建异步回调路由,修改文件:app/api/route/v1.php,在顶部中增加以下代码,别忘了创建对应的AlipayController文件

Route::any('alipay/notify', 'v1.alipay.AlipayController/notify');//支付宝支付回调

首先需要处理支付包异步返回来的数据,新增aliPayNotify()方法来解析处理异步回调返回的参数。

	/**
* 支付宝异步回调
* @param callable $notifyFn 闭包函数 参数1,回调返回的参数,回调结果
* @return bool
*/
protected function aliPayNotify(callable $notifyFn)
{
$post = app()->request->post();
$result = $this->aliPaycheck($post);
if ($result) {
//商户订单号
$post['out_trade_no'] = isset($post['out_trade_no']) ? $post['out_trade_no'] : '';
//支付宝交易号
$post['trade_no'] = isset($post['trade_no']) ? $post['trade_no'] : '';
//交易状态
$post['trade_status'] = isset($post['trade_status']) ? $post['trade_status'] : '';
//备注
$post['attach'] = isset($post['passback_params']) ? urldecode($post['passback_params']) : '';
//异步回调成功执行
try {
if (is_callable($notifyFn)) $notifyFn((object)$post, $result);
} catch (\Exception $e) {
$this->isLog && $this->writeLog('支付宝支付成功,订单号为:' . $post['out_trade_no'] . '.回调报错:' . $e->getMessage());
}
echo 'success';
} else {
echo 'fail';
}
$this->isLog && $this->writeLog($result);
return true; }

执行instance()方法实例本类调用aliPayNotify()方法

	/**
* 支付宝支付同步回调
*/
public static function aliPayReturn()
{
self::instance()->aliPayNotify(function ($data, $result) {
//$data为支付宝回调返回通过解析后重组的数据
//$result为验签结果
//这里要写异步回调的逻辑
//可根据$data->attach来判断需要执行的逻辑
//可以调用TP6中的事件来执行对应的业务逻辑
//event($data->attach,$data)
});
}

同步回调

同步回调使用在支付成功后跳转回去的页面一遍验证下返回的数据是否正常,记录下支付宝订单号,或者其他逻辑

	/**
* 支付支付同步回调
*/
public static function handleNotify()
{
//获取返回参数
$get = app()->request->get();
//验签成功与否
$result = self::instance()->aliPaycheck($get);
//记录日志
self::instance()->isLog && self::instance()->writeLog(compact('result', 'get'));
return compact('result', 'get');
}

其他的业务逻辑可根据自身需求编写,以上就是支付宝支付的下单,退款,异步同步回调

记得进入后台增加对应的支付宝配置.

TinkPHP框架开发的CRMEB小程序商城v4.0二次开发集成支付宝支付的更多相关文章

  1. CRMEB小程序商城v4.0二次开发对接集成阿里云短信

    作者:廖飞 - CRMEB小程序商城研发项目组长 前言 cremb小程序商城v4.0版本支持短信平台为云信,但有部分用户有需求对接阿里云短信,这篇文章将对阿里云短信平台如何对接方以及对接流程详细说明. ...

  2. “微信小程序商城构建全栈应用”开发小记

    注意事项: 1.application\api\extra下的wx.php记得填写小程序的app_id.app_secret: 2.API测试小工具需要APPID:

  3. CRMEB小程序商城首页强制在微信中打开解决办法

    先说一下,这也算不上二开,小小修改一下而已. CRMEB安装完成后,PC端直接打开首页,真是一言难尽~ 然后,我就想了,用手机浏览器或者PC浏览器直接打开首页也没啥用,干脆直接强制在微信中打开算了! ...

  4. 《微信小程序商城开发实战》笔者的新书,欢迎各位粉丝上京东购买

    作者图书京东链接,请点击------>>>    **微信小程序商城开发实战** 附京东真实评价截图: 编辑推荐 在当今移动互联网大潮中,微信应用凭借其庞大的用户基数和极强的用户黏性 ...

  5. 基于NopCommerce框架开发的微信小程序UrShop

    Urshop小程序商城 介绍 UrShop小程序商城 2.0发布啦,发布地址https://gitee.com/urselect/urshop UrShop 根据NopCommerce框架开发的,基于 ...

  6. 微信小程序商城开源项目,Weixin-App-Shop 1.0 版本正式发布!!!

    微信小程序商城开源项目,Weixin-App-Shop 1.0 版本正式发布 Weixin-App-Shop 是捷微团队开发的微信小程序商城开源项目,涵盖了微信商城的全部功能,能够快速发布简单易用的小 ...

  7. TP5框架,开源小程序商城源码,前端+后台完整版

    CRMEB微信公众号商城小程序商城数据同步,带积分.优惠券.秒杀.砍价.分销等功能,更是一套方便二次开发的框架 开源地址:https://github.crmeb.net/u/crmeb 商城演示后台 ...

  8. 零基础开发一款微信小程序商城

    零基础开发一款微信小程序商城 一个朋友问我能不能帮忙做个商城?我一个完整网页都写不出的 菜鸟程序员,我该怎么拒绝呢?好吧,看在小程序这么火的形势下,我还是答应了!找了个开源项目,差不多花了三天时间搞定 ...

  9. 使用wepy开发微信小程序商城第二篇:路由配置和页面结构

    使用wepy开发微信小程序商城 第二篇:路由配置和页面结构 前言: 最近公司在做一个微信小程序的项目,用的是类似于vue的wepy框架.我也借此机会学习和实践一下. 小程序官方文档:https://d ...

随机推荐

  1. Pintech品致全新多功能MDO 704E系列示波器全新推出

    2020年 7月,Pintech品致全新推出推出首款具有多个模拟通道和多个数字通道的示波器.每个模拟通道带宽为200 MHz,每个模拟通道采样率同时达1 GSa/s,在一台仪器中,实现精确.可重复的. ...

  2. 精通python网络爬虫PDF高清完整版免费下载|百度云盘|Python基础教程免费电子书

    点击获取书籍提取码:yc9w

  3. 简单python爬虫编写,Python采集妹子图!

    疫情期间在家闲来无事,每天打游戏荒废了一段时间.我觉得自己不能在这么颓废下去,就立马起身写了一点python代码(本人只是python新手). 很多人学习python,不知道从何学起.很多人学习pyt ...

  4. [机器学习 ]PCA降维--两种实现 : SVD或EVD. 强力总结. 在鸢尾花数据集(iris)实做

    PCA降维--两种实现 : SVD或EVD. 强力总结. 在鸢尾花数据集(iris)实做 今天自己实现PCA,从网上看文章的时候,发现有的文章没有搞清楚把SVD(奇异值分解)实现和EVD(特征值分解) ...

  5. 【模式识别与机器学习】——3.5Fisher线性判别

    ---恢复内容开始--- 出发点 应用统计方法解决模式识别问题时,一再碰到的问题之一就是维数问题. 在低维空间里解析上或计算上行得通的方法,在高维空间里往往行不通. 因此,降低维数有时就会成为处理实际 ...

  6. C# 使用代理实现线程间调用

    实现功能: 后台线程改变窗体控件(flowLayoutPanel1)的状态. 利用 this.flowLayoutPanel1.InvokeRequired == false,可以知道是主线程调用的自 ...

  7. Java 语法 try catch使用容易忽略的细节 BigDecimal

    try catch使用细节 一. try catch的使用方式容易理解,两者最终都要执行finally中的代码,而当return在try和catch中又会有什么效果? 如果我们做一个简单的例子就会发现 ...

  8. printf函数和putchar函数

    #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> #include<stdlib. ...

  9. 微信支付.NET SDK 中的BUG(存疑)

    BUG出现在类文件WxPayData.cs中的FromXml(string xml)方法 /** * @将xml转为WxPayData对象并返回对象内部的数据 * @param string 待转换的 ...

  10. Linux学习笔记 一 第二章 Linux系统安装

    Linux系统安装 一.首先安装VMware 虚拟机 下载网址:https://www.vmware.com/cn/products/workstation-pro/workstation-pro-e ...