Wallogit.com
2017 © Pedro Peláez
Faycms支付模块
先放调用代码,因为调用代码很简单。如果不扩展支付方式的话,后面那一大段都不需要看。
调用代码分三步:
- 创建交易
- 发起支付
- 监听支付成功事件, (*1)
//创建交易
$trade_id = TradeService::service()->create(
1,//金额,单位:分
'测试订单',
array(
array(
'type'=>1,//类型,在业务中定义
'refer_id'=>123,//关联id,可以是用户Id,订单Id等,具体看业务逻辑
)
),
array(//这里是可选参数,可以根据业务逻辑指定需要用到的字段
'return_url'=>UrlHelper::createUrl('test/pay-result'),//支付回调页
)
);
直接通过api调用发起支付。该接口根据支付方式不同,可能返回json数据,也可能直接跳转到第三方支付页面。, (*2)
{$base_url}faypay/api/payment/pay?trade_id={$交易ID}&payment_id={$支付方式ID}
根据支付方式不同,有的支付方式是通过ajax等方式请求接口,有的支付方式是将页面跳转到接口。, (*3)
通过configs/events.php注册监听事件, (*4)
return array(
//充值成功事件
\faypay\services\trade\TradePaymentService::EVENT_PAID => array(
array(
'handler'=>function(\faypay\services\trade\TradePaymentItem $tradePaymentItem){
//@todo 业务逻辑
}
),
),
)
支付方式是一个独立的模块。支付方式是独立于交易(Trade)的,可以通过其他方式构造支付参数,发起支付。本系统出于业务逻辑考虑,都是由交易(Trade)模块调起支付。, (*5)
PaymentMethodConfigModel:支付方式配置信息(在构建支付的时候传入此类实例)
code:支付方式编码。例如:weixin:jsapi(微信支付:jsapi支付)【必选】sign_type:签名方式。有些字符方式有这个选项【可选】app_id:对应微信支付:公众号ID(app_id);支付宝:合作者身份ID(partner);银联没有这个值mch_id:对应微信支付:商户号(mch_id);支付宝:卖家支付宝用户好(seller_id);银联:商户号(merId)key:商户支付密钥。对应微信支付:商户支付密钥(key)app_secret:公众帐号app_secret(目前只有微信jsapi支付有这个参数)PaymentTradeModel:交易信息(在构建支付的时候传入此类实例)
notify_url:服务器异步通知页面路径。微信和支付宝对应:notify_url;银联对应:backUrl
return_url:页面跳转同步通知页面路径(网页支付会有这个地址,app支付一般没有这个地址)。支付宝对应:return_url;银联对应:frontUrl
out_trade_no:商户订单号。所有支付方式都有这个字段(但不同的支付方式对格式有一定的要求)。第三方支付方式视为唯一标识,同一个商户订单号不能重复支付。total_fee:支付金额(以“分”为单位的整数,如果支付方式需要传入以“元”为单位的值,需要转化)。微信对应:total_fee,单位:分;支付宝对应:total_fee,单位:元;银联对应:txnAmt:单位:分body:交易描述。微信支付:简要描述(body);对应支付宝:商品描述(body);银联:订单描述(orderDesc)subject:交易标题。对应支付宝:订单标题(subject)show_url:对应支付宝:商品展示网址(show_url)it_b_pay:对应支付宝:超时时间(it_b_pay)attach:透传字段。对应微信支付:附加数据(attach);银联:请求方保留域(reqReserved)time_start:订单生成时间(strtotime能识别的时间格式都行),一般默认为当前时间即可,不需要填写time_expire:订单失效时间(strtotime能识别的时间格式都行)trade_payment_id:交易支付记录ID(并不属于支付需要用到的字段,但是做微信支付OAuth认证的时候需要做跳转,要用到这个字段)示例代码:, (*6)
//从数据库获取支付方式配置信息
$payment_method = $trade_payment->getPaymentMethod();
//实例化用于支付的支付方式配置模型
$payment_config = new PaymentMethodConfigModel($payment_method['code']);
//设置相关属性
$payment_config->setMchId($payment_method['config']['mch_id'])
->setAppId($payment_method['config']['app_id'])
->setAppSecret($payment_method['config']['app_secret'])
->setKey($payment_method['config']['key'])
;
//获取交易信息
$trade = $trade_payment->getTrade();
//实例化用于支付的交易数据模型
$payment_trade = new PaymentTradeModel();
//设置相关属性
$payment_trade->setOutTradeNo($trade_payment->getOutTradeNo())
->setTotalFee($trade_payment->total_fee)
->setNotifyUrl(UrlHelper::createUrl('api/payment/notify/code/'.$payment_method['code']))
->setBody($trade->body)
->setTradePaymentId($trade_payment->id)
->setReturnUrl($trade->return_url)
->setShowUrl($trade->show_url)
;
//调用支付模块。根据支付方式不同,此方法可能返回json数据,也可能直接跳转到支付页面
PaymentMethodService::service()->buildPay($payment_trade, $payment_config);
实现ArrayAccess接口和__get()/__set()方法。支持以数组或对象属性的方式获取或修改trades表对应字段信息。, (*7)
save():字段被修改后调用此方法可以保存到数据库getRefers():获取交易关联信息,对应trade_refers表字段getTrade():获取交易信息。对应trades表字段pay():发起支付与数据库打交道的服务类。用于创建、获取交易信息。, (*8)
get():根据交易ID,获取TradeItem实例create():创建一笔交易。参数含义如下:
$total_fee:交易金额(单位:分)$body:交易描述$refers:关联信息。二维数组,每项必须包含type和refer_id字段$extra:键值数组,可选择包含字段:subject, expire_time, return_url, show_url
$user_id:用户ID,若为null,则默认为当前登录用户交易状态变更采用状态模式设计。每个状态对应state文件夹下的状态类。状态类均继承自StateInterface接口。StateInterface接口含以下方法:, (*9)
pay():执行支付refund():执行退款close():交易关闭实现ArrayAccess接口和__get()/__set()方法。支持以数组或对象属性的方式获取或修改trades表对应字段信息。, (*10)
save():字段被修改后调用此方法可以保存到数据库getTrade():获取交易信息,返回TradeItem实例setTrade():在实例化TradePayment时,并不会马上根据交易记录里的trade_id去初始化trade属性。为了节省开销,可以将已经实例化的TradeItem实例设置进去。若不设置,在调用getTrade()时会根据trade_id自动初始化。getPaymentMethod(TradeItem $trade):获取支付方式信息,返回faypay\services\methods\PaymentMethodService::get()的结果setPaymentMethod(array $payment_method):在实例化TradePayment时,并不会马上根据交易记录里的payment_method_id去初始化payment_method属性。为了节省开销,可以将已获取的支付方式信息设置进去。若不设置,在调用getPaymentMethod()时会根据payment_method_id自动初始化。getOutTradeNo():生成一个唯一的外部订单号onPaid():支付完成处理pay():发起支付与数据库打交道的服务类。用于创建、获取交易支付记录。, (*11)
create():创建一条交易支付记录
$trade_id:交易ID$total_fee:支付金额(单位:分)$payment_method_id:支付方式IDget():根据支付记录ID,获取一个支付记录实例(TradePaymentItem)getByOutTradeNo():根据外部订单号,获取支付记录实例(TradePaymentItem)支付记录状态变更采用状态模式设计。每个状态对应payment_state文件夹下的状态类。状态类均继承自PaymentStateInterface接口。PaymentStateInterface接口含以下方法:, (*12)
pay():执行支付onPaid():接受支付回调refund():交易支付记录执行退款(有可能是重复支付产生的退款,并不一定就是对交易进行退款)close():交易支付记录关闭交易(Trade)与交易支付记录(TradePayment)是一对多的关系。每发生一次支付行为,就会产生一条支付记录。当有一条支付记录变为已付款后,其他同交易的支付记录都会变为已关闭。, (*13)
DROP TABLE IF EXISTS `{{$prefix}}payments`;
CREATE TABLE `{{$prefix}}payments` (
`id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Id',
`code` varchar(20) NOT NULL DEFAULT '' COMMENT '支付编码',
`name` varchar(50) NOT NULL DEFAULT '' COMMENT '支付名称',
`description` varchar(500) NOT NULL DEFAULT '' COMMENT '支付描述',
`enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用',
`config` text COMMENT '配置信息JSON',
`create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`update_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'Deleted',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET={{$charset}} COMMENT='付款方式';
DROP TABLE IF EXISTS `{{$prefix}}trades`;
CREATE TABLE `{{$prefix}}trades` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Id',
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`subject` varchar(255) NOT NULL DEFAULT '' COMMENT '支付说明',
`body` varchar(255) NOT NULL DEFAULT '' COMMENT '支付描述',
`total_fee` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT '付款金额(单位:分)',
`paid_fee` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT '已付金额(单位:分)',
`trade_payment_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '支付记录ID(付成功的那条)',
`refund_fee` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT '退款金额(单位:分)',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '支付状态',
`create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`expire_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '过期时间',
`pay_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '付款时间',
`create_ip` int(11) NOT NULL DEFAULT '0' COMMENT '创建IP',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET={{$charset}} COMMENT='交易记录';
DROP TABLE IF EXISTS `{{$prefix}}trade_refers`;
CREATE TABLE `{{$prefix}}trade_refers` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Id',
`trade_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '交易ID',
`type` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '交易类型',
`refer_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '关联ID',
PRIMARY KEY (`id`),
KEY `trade_id` (`trade_id`)
) ENGINE=InnoDB DEFAULT CHARSET={{$charset}} COMMENT='交易引用关系表';
DROP TABLE IF EXISTS `{{$prefix}}trade_payments`;
CREATE TABLE `{{$prefix}}trade_payments` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Id',
`trade_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '交易ID',
`total_fee` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT '支付金额(单位:分)',
`create_time` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`create_ip` int(11) NOT NULL DEFAULT '0' COMMENT '创建IP',
`paid_time` int(11) NOT NULL DEFAULT '0' COMMENT '支付时间',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '支付状态',
`payment_id` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '支付方式ID',
`trade_no` varchar(255) NOT NULL DEFAULT '' COMMENT '第三方交易号',
`payer_account` varchar(50) NOT NULL DEFAULT '' COMMENT '付款人帐号',
`paid_fee` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT '实付金额(单位:分)',
`refund_fee` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT '退款金额(单位:分)',
PRIMARY KEY (`id`),
KEY `trade_id` (`trade_id`)
) ENGINE=InnoDB DEFAULT CHARSET={{$charset}} COMMENT='交易支付记录表';
out_trade_no: 外部订单号subject: 标题body: 简要描述total_fee: 金额it_b_pay: 有效期(取值比较奇特,参照官方文档)notify_url: 回调地址return_url: 同步跳转地址show_url: 商户交易信息地址out_trade_no: 外部订单号body: 简要描述total_fee: 金额notify_url: 回调地址attach: 透传字段trade_type:交易类型:JSAPI,NATIVE,APPfrontUrl: 同步跳转地址backUrl: 异步回调地址orderId: 外部订单号txnAmt: 金额orderDesc: 简要描述reqReserved: 透传字段