前言

大家都知道支付宝支付和微信支付宝都只能局限在自己的平台,微信内支付宝支付是根本就不能使用,即使是公众号支付也需要跳转到外部浏览器才可以唤起支付宝支付,并且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. spring中IOC入口与加载步骤

    ApplicationContext applicationContext =new ClassPathXmlApplicationContext("spring-context.xml&q ...

  2. 数据量大了一定要分表,分库分表组件Sharding-JDBC入门与项目实战

    最近项目中不少表的数据量越来越大,并且导致了一些数据库的性能问题.因此想借助一些分库分表的中间件,实现自动化分库分表实现.调研下来,发现Sharding-JDBC目前成熟度最高并且应用最广的Java分 ...

  3. 关于idea 在创建maven 骨架较慢问题解决

    在设置中->maven>runner>VM Options 粘贴     -DarchetypeCatalog=internal 其中 -D archetype:原型,典型的意思 ( ...

  4. 19-关键字package和import

    1. package的使用1.1 使用说明: * 1.为了更好的实现项目中类的管理,提供包的概念 * 2.使用package声明类或接口所属的包,声明在源文件的首行 * 3.包,属于标识符,遵循标识符 ...

  5. 「MoreThanJava」Day 4:面向对象基础

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...

  6. Go之Gorm和BeegoORM简介及配置使用

    简介 ORM Object-Relationl Mapping, 它的作用是映射数据库和对象之间的关系,方便我们在实现数据库操作的时候不用去写复杂的sql语句,把对数据库的操作上升到对于对象的操作 G ...

  7. 【模式识别与机器学习】——PART2 机器学习——统计学习基础——Regularized Linear Regression

    来源:https://www.cnblogs.com/jianxinzhou/p/4083921.html 1. The Problem of Overfitting (1) 还是来看预测房价的这个例 ...

  8. 小程序3:ATM小程序

    ATM实现流程 1.欢迎界面和菜单 显示部分的代码代码放到view.py中 import time class View(): def login(): """ 管理员登 ...

  9. 【数论】莫比乌斯反演Mobius inversion

    本文同步发布于作业部落,若想体验更佳,请点此查看原文.//博客园就是渣,连最基本的符号都打不出来.

  10. andriod开发中遇到的错误

    1.java.net.UnknownServiceException: CLEARTEXT communication ** not permitted by network security pol ...