<?php /** * @Author: [FENG] <1161634940@qq.com> * @Date: 2020-05-13 17:02:49 * @Last Modified by: [FENG] <1161634940@qq.com> * @Last Modified time: 2021-06-15T17:06:18+08:00 */ namespace fengkui\Pay; use fengkui\Supports\Http; /** * Bytedance 字节跳动支付 * 小程序担保支付(V1) */ class Bytedance { // 接口版本 const EDITON = 'v1' ; // 统一下订单管理 private static $ecpayUrl = 'https://developer.toutiao.com/api/apps/ecpay/' ; // 服务端预下单 private static $createOrderUrl = 'https://developer.toutiao.com/api/apps/ecpay/v1/create_order' ; // 订单查询 private static $queryOrderUrl = 'https://developer.toutiao.com/api/apps/ecpay/v1/query_order' ; // 退款 private static $createRefundUrl = 'https://developer.toutiao.com/api/apps/ecpay/v1/create_refund' ; // 查询退款 private static $queryRefundUrl = 'https://developer.toutiao.com/api/apps/ecpay/v1/query_refund' ; // 分账请求 private static $settleUrl = 'https://developer.toutiao.com/api/apps/ecpay/v1/settle' ; // 查询分账 private static $querySettleUrl = 'https://developer.toutiao.com/api/apps/ecpay/v1/query_settle' ; // 服务商进件 private static $addMerchantUrl = 'https://developer.toutiao.com/api/apps/ecpay/saas/add_merchant' ; // 分账方进件 private static $addSubMerchantUrl = 'https://developer.toutiao.com/api/apps/ecpay/saas/add_sub_merchant' ; // 支付相关配置 private static $config = array ( 'app_id' => '' , // App ID 'salt' => '' , // 支付密钥值 'notify_url' => '' , // 支付回调地址 'thirdparty_id' => '' , // 第三方平台服务商 id,非服务商模式留空 ); /** * [__construct 构造函数] * @param [type] $config [传递支付相关配置] */ public function __construct( $config =NULL){ $config && self:: $config = array_merge (self:: $config , $config ); } /** * [createOrder 下单支付] * @param [type] $order [description] * @return [type] [description] * $order = array( * 'body' => '', // 产品描述 * 'total_amount' => '', // 订单金额(分) * 'order_sn' => '', // 订单编号 * ); */ public static function createOrder( $order ) { $config = self:: $config ; $params = [ 'app_id' => $config [ 'app_id' ], // 是 小程序 AppID 'out_order_no' => (string) $order [ 'order_sn' ], // 是 开发者侧的订单号, 同一小程序下不可重复 'total_amount' => $order [ 'total_amount' ], // 是 支付价格; 接口中参数支付金额单位为[分] 'subject' => $order [ 'body' ], // 是 商品描述; 长度限制 128 字节,不超过 42 个汉字 'body' => $order [ 'body' ], // 是 商品详情 'valid_time' => 3600 * 2, // 是 订单过期时间(秒); 最小 15 分钟,最大两天 // 'sign' => '', // 是 开发者对核心字段签名, 签名方式见文档附录, 防止传输过程中出现意外 // 'cp_extra' => '', // 否 开发者自定义字段,回调原样回传 // 'notify_url' => $config['notify_url'], // 否 商户自定义回调地址 // 'thirdparty_id' => '', // 否 第三方平台服务商 id,非服务商模式留空 'disable_msg' => 1, // 否 是否屏蔽担保支付的推送消息,1-屏蔽 0-非屏蔽,接入 POI 必传 // 'msg_page' => '', // 否 担保支付消息跳转页 // 'store_uid' => '', // 否 多门店模式下,门店 uid ]; ! empty ( $order [ 'cp_extra' ]) && $params [ 'cp_extra' ] = $order [ 'cp_extra' ]; ! empty ( $config [ 'notify_url' ]) && $params [ 'notify_url' ] = $config [ 'notify_url' ]; ! empty ( $config [ 'thirdparty_id' ]) && $params [ 'thirdparty_id' ] = $config [ 'thirdparty_id' ]; if (! empty ( $config [ 'msg_page' ])) { $params [ 'disable_msg' ] = 0; $params [ 'msg_page' ] = $config [ 'msg_page' ]; } $params [ 'sign' ] = self::makeSign( $params ); // dump($params);die; $url = self:: $createOrderUrl ; $response = Http::post( $url , json_encode( $params )); $result = json_decode( $response , true); return $result ; } /** * [queryOrder 订单查询] * @param [type] $orderSn [开发者侧的订单号, 不可重复] * @return [type] [description] */ public static function queryOrder( $orderSn ) { $config = self:: $config ; $params = [ 'app_id' => $config [ 'app_id' ], // 小程序 AppID 'out_order_no' => (string) $orderSn , // 开发者侧的订单号, 不可重复 // 'sign' => '', // 开发者对核心字段签名, 签名方式见文档, 防止传输过程中出现意外 // 'thirdparty_id' => '', // 服务商模式接入必传 第三方平台服务商 id,非服务商模式留空 ]; ! empty ( $config [ 'thirdparty_id' ]) && $params [ 'thirdparty_id' ] = $config [ 'thirdparty_id' ]; $params [ 'sign' ] = self::makeSign( $params ); $url = self:: $queryOrderUrl ; $response = Http::post( $url , json_encode( $params )); $result = json_decode( $response , true); return $result ; } /** * [notifyOrder 订单回调验证] * @return [array] [返回数组格式的notify数据] */ public static function notifyOrder() { $data = $_POST ; // 获取回调数据 $config = self:: $config ; if (! $data || empty ( $data [ 'msg' ])) die ( '暂无回调信息' ); $result = json_decode( $data [ 'msg' ], true); // 进行签名验证 // 判断签名是否正确 判断支付状态 if ( $result && $data [ 'type' ]== 'payment' ) { return $data ; } else { return false; } } /** * [createRefund 订单退款] * @param [type] $order [订单相关信息] * @return [type] [description] * $order = array( * 'order_sn' => '', // 订单编号 * 'refund_sn' => '', // 退款编号 * 'total_amount' => '', // 订单金额(分) * 'body' => '', // 退款原因 * ); */ public static function createRefund( $order ) { $config = self:: $config ; $params = [ 'app_id' => $config [ 'app_id' ], // 是 小程序 id 'out_order_no' => (string) $order [ 'order_sn' ], // 是 商户分配订单号,标识进行退款的订单 'out_refund_no' => (string) $order [ 'refund_sn' ], // 是 商户分配退款号 'refund_amount' => $order [ 'total_amount' ], // 是 退款金额,单位[分] 'reason' => $order [ 'body' ] ?? '用户申请退款' , // 是 退款理由,长度上限 100 // 'cp_extra' => '', // 否 开发者自定义字段,回调原样回传 // 'notify_url' => '', // 否 商户自定义回调地址 // 'sign' => '', // 是 开发者对核心字段签名, 签名方式见文档, 防止传输过程中出现意外 // 'thirdparty_id' => '', // 否,服务商模式接入必传 第三方平台服务商 id,非服务商模式留空 'disable_msg' => 1, // 否 是否屏蔽担保支付消息,1-屏蔽 // 'msg_page' => '', // 否 担保支付消息跳转页 // 'all_settle' => '', // 否 是否为分账后退款,1-分账后退款;0-分账前退款。分账后退款会扣减可提现金额,请保证余额充足 ]; ! empty ( $order [ 'cp_extra' ]) && $params [ 'cp_extra' ] = $order [ 'cp_extra' ]; ! empty ( $order [ 'all_settle' ]) && $params [ 'all_settle' ] = $order [ 'all_settle' ]; ! empty ( $config [ 'thirdparty_id' ]) && $params [ 'thirdparty_id' ] = $config [ 'thirdparty_id' ]; if (! empty ( $config [ 'msg_page' ])) { $params [ 'disable_msg' ] = 0; $params [ 'msg_page' ] = $config [ 'msg_page' ]; } $params [ 'sign' ] = self::makeSign( $params ); $url = self:: $queryOrderUrl ; $response = Http::post( $url , json_encode( $params )); $result = json_decode( $response , true); return $result ; } /** * [queryRefund 退款查询] * @param [type] $refundSn [开发者侧的订单号, 不可重复] * @return [type] [description] */ public static function queryRefund( $refundSn ) { $config = self:: $config ; $params = [ 'app_id' => $config [ 'app_id' ], // 小程序 AppID 'out_refund_no' => $refundSn , // 开发者侧的退款号 // 'sign' => '', // 开发者对核心字段签名, 签名方式见文档, 防止传输过程中出现意外 // 'thirdparty_id' => '', // 服务商模式接入必传 第三方平台服务商 id,非服务商模式留空 ]; ! empty ( $config [ 'thirdparty_id' ]) && $params [ 'thirdparty_id' ] = $config [ 'thirdparty_id' ]; $params [ 'sign' ] = self::makeSign( $params ); $url = self:: $queryRefundUrl ; $response = Http::post( $url , json_encode( $params )); $result = json_decode( $response , true); return $result ; } /** * [notifyRefund 退款回调验证] * @return [array] [返回数组格式的notify数据] */ public static function notifyRefund() { $data = $_POST ; // 获取回调数据 $config = self:: $config ; if (! $data || empty ( $data [ 'status' ])) die ( '暂无回调信息' ); $result = json_decode( $data [ 'msg' ], true); // 进行签名验证 // 判断签名是否正确 判断支付状态 if ( $result && $data [ 'status' ]!= 'FAIL' ) { return $data ; } else { return false; } } /** * [settle 分账请求] * @param [type] $order [分账信息] * @return [type] [description] * $order = array( * 'body' => '', // 产品描述 * 'total_amount' => '', // 订单金额(分) * 'order_sn' => '', // 订单编号 * ); */ public static function settle( $order ) { $config = self:: $config ; $params = [ 'app_id' => $config [ 'app_id' ], // 是 小程序 AppID 'out_order_no' => (string) $order [ 'order_sn' ], // 是 商户分配订单号,标识进行结算的订单 'out_settle_no' => (string) $order [ 'settle_sn' ], // 是 开发者侧的结算号, 不可重复 'settle_desc' => $order [ 'body' ], // 是 结算描述,长度限制 80 个字符 // 'cp_extra' => '', // 否 开发者自定义字段,回调原样回传 // 'notify_url' => '', // 否 商户自定义回调地址 // 'sign' => '', // 是 开发者对核心字段签名, 签名方式见文档, 防止传输过程中出现意外 // 'thirdparty_id' => '', // 否,服务商模式接入必传 第三方平台服务商 id,非服务商模式留空 // 'settle_params' => '', // 否,其他分账方信息,分账分配参数 SettleParameter 数组序列化后生成的 json 格式字符串 ]; ! empty ( $order [ 'cp_extra' ]) && $params [ 'cp_extra' ] = $order [ 'cp_extra' ]; ! empty ( $order [ 'settle_params' ]) && $params [ 'settle_params' ] = $order [ 'settle_params' ]; ! empty ( $config [ 'thirdparty_id' ]) && $params [ 'thirdparty_id' ] = $config [ 'thirdparty_id' ]; $params [ 'sign' ] = self::makeSign( $params ); $url = self:: $settleUrl ; $response = Http::post( $url , json_encode( $params )); $result = json_decode( $response , true); return $result ; } /** * [querySettle 分账查询] * @param [type] $settleSn [开发者侧的订单号, 不可重复] * @return [type] [description] */ public static function querySettle( $settleSn ) { $config = self:: $config ; $params = [ 'app_id' => $config [ 'app_id' ], // 小程序 AppID 'out_settle_no' => $settleSn , // 开发者侧的分账号 // 'sign' => '', // 开发者对核心字段签名, 签名方式见文档, 防止传输过程中出现意外 // 'thirdparty_id' => '', // 服务商模式接入必传 第三方平台服务商 id,非服务商模式留空 ]; ! empty ( $config [ 'thirdparty_id' ]) && $params [ 'thirdparty_id' ] = $config [ 'thirdparty_id' ]; $params [ 'sign' ] = self::makeSign( $params ); $url = self:: $querySettleUrl ; $response = Http::post( $url , json_encode( $params )); $result = json_decode( $response , true); return $result ; } /** * [notifySettle 分账回调验证] * @return [array] [返回数组格式的notify数据] */ public static function notifySettle() { $data = $_POST ; // 获取回调数据 $config = self:: $config ; if (! $data || empty ( $data [ 'status' ])) die ( '暂无回调信息' ); $result = json_decode( $data [ 'msg' ], true); // 进行签名验证 // 判断签名是否正确 判断支付状态 if ( $result && $data [ 'status' ]!= 'FAIL' ) { return $data ; } else { return false; } } /** * [addMerchant 服务商进件] * @param [type] $accessToken [授权码兑换接口调用凭证] * @param [type] $componentId [小程序第三方平台应用] * @param integer $urlType [链接类型:1-进件页面 2-账户余额页] */ public static function addMerchant( $accessToken , $componentId , $urlType =1) { $params = [ 'component_access_token' => $accessToken , // 是 授权码兑换接口调用凭证 'thirdparty_component_id' => $componentId , // 是 小程序第三方平台应用 id 'url_type' => $urlType , // 是 链接类型:1-进件页面 2-账户余额页 ]; $url = self:: $addMerchantUrl ; $response = Http::post( $url , json_encode( $params )); $result = json_decode( $response , true); return $result ; } /** * [addSubMerchant 分账方进件] * @param [type] $thirdpartyId [小程序第三方平台应用] * @param [type] $merchantId [商户 id,用于接入方自行标识并管理进件方。由服务商自行分配管理] * @param integer $urlType [链接类型:1-进件页面 2-账户余额页] */ public static function addSubMerchant( $thirdpartyId , $merchantId , $urlType =1) { $params = [ 'thirdparty_id' => $thirdpartyId , // 是 小程序第三方平台应用 id 'sub_merchant_id' => $merchantId , // 是 商户 id,用于接入方自行标识并管理进件方。由服务商自行分配管理 'url_type' => $urlType , // 是 链接类型:1-进件页面 2-账户余额页 // 'sign' => '', // 开发者对核心字段签名, 签名方式见文档, 防止传输过程中出现意外 ]; $params [ 'sign' ] = self::makeSign( $params ); $url = self:: $addSubMerchantUrl ; $response = Http::post( $url , json_encode( $params )); $result = json_decode( $response , true); return $result ; } /** * [success 通知状态] */ public static function success() { $array = [ 'err_no' =>0, 'err_tips' => 'success' ]; die (json_encode( $array )); } /** * [makeSign 生成秘钥] * @param [type] $data [加密数据] * @return [type] [description] */ public static function makeSign( $data ) { $config = self:: $config ; $rList = array (); foreach ( $data as $k => $v ) { if ( $k == "other_settle_params" || $k == "app_id" || $k == "sign" || $k == "thirdparty_id" ) continue ; $value = trim( strval ( $v )); $len = strlen ( $value ); if ( $len > 1 && substr ( $value , 0,1)== "\"" && substr ( $value , $len , $len -1)== "\"" ) $value = substr ( $value ,1, $len -1); $value = trim( $value ); if ( $value == "" || $value == "null" ) continue ; array_push ( $rList , $value ); } array_push ( $rList , $config [ 'salt' ]); sort( $rList , 2); return md5(implode( '&' , $rList )); } } |