Files
shop-platform/src/addon/weapp/model/Weapp.php

1069 lines
45 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
*/
namespace addon\weapp\model;
use addon\wechatpay\model\Config as WechatPayModel;
use app\model\BaseModel;
use app\model\order\Order;
use app\model\shop\Shop as ShopModel;
use app\model\system\Api;
use EasyWeChat\Factory;
use think\facade\Cache;
use addon\weapp\model\Config as WeappConfigModel;
use addon\wxoplatform\model\Config as WxOplatformConfigModel;
use app\model\web\Config as WebConfig;
use think\facade\Log;
/**
* 微信小程序配置
*/
class Weapp extends BaseModel
{
private $app;//微信模型
//小程序类型
public $service_type = array (
0 => "小程序",
);
//小程序认证类型
public $verify_type = array (
-1 => "未认证",
0 => "微信认证",
);
//business_info 说明
public $business_type = array (
'open_store' => "是否开通微信门店功能",
'open_scan' => "是否开通微信扫商品功能",
'open_pay' => "是否开通微信支付功能",
'open_card' => "是否开通微信卡券功能",
'open_shake' => "是否开通微信摇一摇功能",
);
// 站点ID
private $site_id;
public function __construct($site_id = 0)
{
$this->site_id = $site_id;
//微信小程序配置
$weapp_config_model = new WeappConfigModel();
$weapp_config = $weapp_config_model->getWeappConfig($site_id)[ "data" ][ "value" ];
if (isset($weapp_config[ 'is_authopen' ]) && addon_is_exit('wxoplatform')) {
$plateform_config_model = new WxOplatformConfigModel();
$plateform_config = $plateform_config_model->getOplatformConfig()[ "data" ][ "value" ];
$config = [
'app_id' => $plateform_config[ "appid" ] ?? '',
'secret' => $plateform_config[ "secret" ] ?? '',
'token' => $plateform_config[ "token" ] ?? '',
'aes_key' => $plateform_config[ "aes_key" ] ?? '',
'log' => [
'level' => 'debug',
'permission' => 0777,
'file' => 'runtime/log/wechat/oplatform.logs',
],
];
$open_platform = Factory::openPlatform($config);
$this->app = $open_platform->miniProgram($weapp_config[ 'authorizer_appid' ], $weapp_config[ 'authorizer_refresh_token' ]);
} else {
$config = [
'app_id' => $weapp_config[ "appid" ] ?? '',
'secret' => $weapp_config[ "appsecret" ] ?? '',
'response_type' => 'array',
'log' => [
'level' => 'debug',
'permission' => 0777,
'file' => 'runtime/log/wechat/easywechat.logs',
],
];
$this->app = Factory::miniProgram($config);
}
}
/**
* TODO
* 根据 jsCode 获取用户 session 信息
* @param $param
* @throws \EasyWeChat\Kernel\Exceptions\InvalidConfigException
*/
public function authCodeToOpenid($param)
{
try {
$result = $this->app->auth->session($param[ 'code' ]);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->handleError($result[ 'errcode' ], $result[ 'errmsg' ]);
} else {
Cache::set('weapp_' . $result[ 'openid' ], $result);
unset($result[ 'session_key' ]);
return $this->success($result);
}
} catch (\Exception $e) {
if (property_exists($e, 'formattedResponse')) {
return $this->handleError($e->formattedResponse[ 'errcode' ], $e->formattedResponse[ 'errmsg' ]);
}
return $this->error('', $e->getMessage());
}
}
/**
* 生成二维码
* @param unknown $param
*/
public function createQrcode($param)
{
try {
$checkpath_result = $this->checkPath($param[ 'qrcode_path' ]);
if ($checkpath_result[ "code" ] != 0) return $checkpath_result;
// scene:场景值最大32个可见字符只支持数字大小写英文以及部分特殊字符!#$&'()*+,/:;=?@-._~
$scene = '';
if (!empty($param[ 'data' ])) {
foreach ($param[ 'data' ] as $key => $value) {
//防止参数过长source_member用m代替
if ($key == 'source_member') {
$key = 'm';
}
if ($scene == '') $scene .= $key . '-' . $value;
else $scene .= '&' . $key . '-' . $value;
}
}
$response = $this->app->app_code->getUnlimit($scene, [
'page' => substr($param[ 'page' ], 1),
'width' => $param['width'] ?? 120
]);
if ($response instanceof \EasyWeChat\Kernel\Http\StreamResponse) {
$filename = $param[ 'qrcode_path' ] . '/';
$filename .= $response->saveAs($param[ 'qrcode_path' ], $param[ 'qrcode_name' ] . '_' . $param[ 'app_type' ] . '.png');
return $this->success([ 'type' => 'weapp', 'path' => $filename ]);
} else {
return $this->handleError($response[ 'errcode' ], $response[ 'errmsg' ]);
}
} catch (\Exception $e) {
if (property_exists($e, 'formattedResponse')) {
return $this->handleError($e->formattedResponse[ 'errcode' ], $e->formattedResponse[ 'errmsg' ]);
}
return $this->error('', $e->getMessage());
}
}
/**
* 校验目录是否可写
* @param unknown $path
* @return multitype:number unknown |multitype:unknown
*/
private function checkPath($path)
{
if (is_dir($path) || mkdir($path, intval('0755', 8), true)) {
return $this->success();
}
return $this->error('', "directory {$path} creation failed");
}
/************************************************************* 数据统计与分析 start **************************************************************/
/**
* 访问日趋势
* @param $from 格式 20170313
* @param $to 格式 20170313
*/
public function dailyVisitTrend($from, $to)
{
try {
$result = $this->app->data_cube->dailyVisitTrend($from, $to);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result[ "list" ]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 访问周趋势
* @param $from
* @param $to
* @return array
*/
public function weeklyVisitTrend($from, $to)
{
try {
$result = $this->app->data_cube->weeklyVisitTrend($from, $to);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result[ "list" ]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 访问月趋势
* @param $from
* @param $to
* @return array
*/
public function monthlyVisitTrend($from, $to)
{
try {
$result = $this->app->data_cube->monthlyVisitTrend($from, $to);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result[ "list" ]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 访问分布
* @param $from
* @param $to
* @return array
*/
public function visitDistribution($from, $to)
{
try {
$result = $this->app->data_cube->visitDistribution($from, $to);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result[ "list" ]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 访问页面
* @param $from
* @param $to
* @return array
*/
public function visitPage($from, $to)
{
try {
$result = $this->app->data_cube->visitPage($from, $to);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result[ "list" ]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/************************************************************* 数据统计与分析 end **************************************************************/
/**
* 下载小程序代码包
* @param $site_id
*/
public function download($site_id)
{
$source_file_path = $this->createTempPackage($site_id, 'public/weapp');
$file_arr = getFileMap($source_file_path);
if (!empty($file_arr)) {
$zipname = 'upload/weapp_' . $site_id . '_' . date('Ymd') . '.zip';
$zip = new \ZipArchive();
$res = $zip->open($zipname, \ZipArchive::CREATE);
if ($res === TRUE) {
foreach ($file_arr as $file_path => $file_name) {
if (is_dir($file_path)) {
$file_path = str_replace($source_file_path . '/', '', $file_path);
$zip->addEmptyDir($file_path);
} else {
$zip_path = str_replace($source_file_path . '/', '', $file_path);
$zip->addFile($file_path, $zip_path);
}
}
$zip->close();
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: Binary");
header("Content-Length: " . filesize($zipname));
header("Content-Disposition: attachment; filename=\"" . basename($zipname) . "\"");
readfile($zipname);
@unlink($zipname);
deleteDir($source_file_path);
}
}
}
/**
* 创建临时包
* @param $site_id
* @param $package_path
* @param string $to_path
* @return string
*/
private function createTempPackage($site_id, $package_path, $to_path = '')
{
if (is_dir($package_path)) {
$package = scandir($package_path);
if (empty($to_path)) {
$to_path = 'upload/temp/' . $site_id . '/';
dir_mkdir($to_path);
}
foreach ($package as $path) {
$temp_path = $package_path . '/' . $path;
if (is_dir($temp_path)) {
if ($path == '.' || $path == '..') {//判断是否为系统隐藏的文件.和.. 如果是则跳过否则就继续往下走,防止无限循环再这里。
continue;
}
dir_mkdir($to_path . $path);
$this->createTempPackage($site_id, $temp_path, $to_path . $path . '/');
} else {
if (file_exists($temp_path)) {
copy($temp_path, $to_path . $path);
if (stristr($temp_path, 'common/vendor.js')) {
$content = file_get_contents($to_path . $path);
$content = $this->paramReplace($site_id, $content);
file_put_contents($to_path . $path, $content);
}
}
}
}
return $to_path;
}
}
/**
* 参数替换
* @param $site_id
* @param $string
* @return null|string|string[]
*/
private function paramReplace($site_id, $string)
{
$api_model = new Api();
$api_config = $api_model->getApiConfig()[ 'data' ];
$web_config_model = new WebConfig();
$web_config = $web_config_model->getMapConfig()[ 'data' ][ 'value' ];
$socket_url = ( strstr(ROOT_URL, 'https://') === false ? str_replace('http', 'ws', ROOT_URL) : str_replace('https', 'wss', ROOT_URL) ) . '/wss';
$patterns = [
'/\{\{\$baseUrl\}\}/',
'/\{\{\$imgDomain\}\}/',
'/\{\{\$h5Domain\}\}/',
'/\{\{\$mpKey\}\}/',
'/\{\{\$apiSecurity\}\}/',
'/\{\{\$publicKey\}\}/',
'/\{\{\$webSocket\}\}/'
];
$replacements = [
ROOT_URL,
ROOT_URL,
ROOT_URL . '/h5',
$web_config[ 'tencent_map_key' ] ?? '',
$api_config[ 'is_use' ] ?? 0,
$api_config[ 'value' ][ 'public_key' ] ?? '',
$socket_url
];
$string = preg_replace($patterns, $replacements, $string);
return $string;
}
/**
* 消息解密
* @param array $param
*/
public function decryptData($param = [])
{
try {
$cache = Cache::get('weapp_' . $param[ 'weapp_openid' ]);
$session_key = $cache[ 'session_key' ] ?? '';
$result = $this->app->encryptor->decryptData($session_key, $param[ 'iv' ], $param[ 'encryptedData' ]);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->handleError($result[ 'errcode' ], $result[ 'errmsg' ]);
}
return $this->success($result);
} catch (\Exception $e) {
if (property_exists($e, 'formattedResponse')) {
return $this->handleError($e->formattedResponse[ 'errcode' ], $e->formattedResponse[ 'errmsg' ]);
}
return $this->error([], $e->getMessage());
}
}
/**
* 获取用户手机号
* @param $code
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function getUserPhoneNumber($code)
{
try {
$result = $this->app->auth->phoneNumber($code);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->handleError($result[ 'errcode' ], $result[ 'errmsg' ]);
}
return $this->success($result[ 'phone_info' ]);
} catch (\Exception $e) {
if (property_exists($e, 'formattedResponse')) {
return $this->handleError($e->formattedResponse[ 'errcode' ], $e->formattedResponse[ 'errmsg' ]);
}
return $this->error([], $e->getMessage());
}
}
/**
* 获取订阅消息template_id
* @param array $param
*/
public function getTemplateId(array $param)
{
try {
$result = $this->app->subscribe_message->addTemplate($param[ 'tid' ], $param[ 'kidList' ], $param[ 'sceneDesc' ]);
return $result;
} catch (\Exception $e) {
return [ 'errcode' => -1, 'errmsg' => $e->getMessage() ];
}
}
/**
* 发送订阅消息
* @param array $param
* @return array
*/
public function sendTemplateMessage(array $param)
{
$result = $this->app->subscribe_message->send([
'template_id' => $param[ 'template_id' ],// 模板id
'touser' => $param[ 'openid' ], // openid
'page' => $param[ 'page' ], // 点击模板卡片后的跳转页面 支持带参数
'data' => $param[ 'data' ] // 模板变量
]);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error($result, $result[ "errmsg" ]);
}
return $this->success($result);
}
/**
* 消息推送
*/
public function relateWeixin()
{
$server = $this->app->server;
$message = $server->getMessage();
Log::write('微信小程序消息推送:' . json_encode($message));
// file_put_contents(__DIR__ . '/debug.txt', var_export($message,true));
if (isset($message[ 'MsgType' ])) {
switch ( $message[ 'MsgType' ] ) {
case 'event':
$this->app->server->push(function($res) {
// file_put_contents(__DIR__ . '/debug1.txt', var_export($res,true));
// 商品审核结果通知
if ($res[ 'Event' ] == 'open_product_spu_audit' && addon_is_exit('shopcomponent', $this->site_id)) {
model('shopcompoent_goods')->update([
'edit_status' => $res[ 'OpenProductSpuAudit' ][ 'status' ],
'reject_reason' => !empty($res[ 'OpenProductSpuAudit' ][ 'reject_reason' ]) ? : '',
'audit_time' => time()
], [
[ 'out_product_id', '=', $res[ 'OpenProductSpuAudit' ][ 'out_product_id' ] ]
]);
}
// 类目审核结果通知
if ($res[ 'Event' ] == 'open_product_category_audit' && addon_is_exit('shopcomponent', $this->site_id)) {
model('shopcompoent_category_audit')->update([
'status' => $res[ 'QualificationAuditResult' ][ 'status' ],
'reject_reason' => !empty($res[ 'QualificationAuditResult' ][ 'reject_reason' ]) ? : '',
'audit_time' => time()
], [
[ 'audit_id', '=', $res[ 'QualificationAuditResult' ][ 'audit_id' ] ]
]);
}
// 视频号支付订单回调
if ($res[ 'Event' ] == 'open_product_order_pay' && addon_is_exit('shopcomponent', $this->site_id)) {
event("shopcomponentNotify", $res);
}
// todo trade_manage_remind_access_api 提醒接入发货信息管理服务API
if ($res[ 'Event' ] == 'trade_manage_remind_access_api' && addon_is_exit('weapp', $this->site_id)) {
Log::write('提醒接入发货信息管理服务APItrade_manage_remind_access_api' . json_encode($res));
}
// todo trade_manage_remind_shipping 提醒需要上传发货信息
if ($res[ 'Event' ] == 'trade_manage_remind_shipping' && addon_is_exit('weapp', $this->site_id)) {
Log::write('提醒需要上传发货信息trade_manage_remind_shipping' . json_encode($res));
}
// todo trade_manage_order_settlement 订单将要结算或已经结算
if ($res[ 'Event' ] == 'trade_manage_order_settlement' && addon_is_exit('weapp', $this->site_id)) {
Log::write('订单将要结算或已经结算trade_manage_order_settlement' . json_encode($res));
}
});
break;
}
}
$response = $this->app->server->serve();
return $response->send();
}
/**
* 检查场景值是否在支付校验范围内
* @param $scene
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function sceneCheck($scene)
{
try {
$result = $this->app->mini_store->checkScene($scene);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] == 0) {
return $this->success($result[ 'is_matched' ]);
} else {
return $this->error('', $result[ 'errmsg' ]);
}
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
public function createOrder($order_info)
{
try {
$result = $this->app->mini_store->addOrder($order_info);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] == 0) {
return $this->success($result[ 'is_matched' ]);
} else {
return $this->error('', $result[ 'errmsg' ]);
}
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 处理错误信息
* @param $errcode
* @param string $message
* @return array
*/
public function handleError($errcode, $message = '')
{
$error = require 'addon/weapp/config/weapp_error.php';
return $this->error([], $error[ $errcode ] ?? $message);
}
/**
* 查询小程序是否已开通发货信息管理服务
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function orderShippingIsTradeManaged()
{
try {
$result = $this->app->order_shipping->isTradeManaged();
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error(false, $result[ "errmsg" ]);
}
return $this->success($result[ 'is_trade_managed' ]);
} catch (\Exception $e) {
return $this->error(false, $e->getMessage());
}
}
/**
* 查询订单列表
* @param array $data
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function orderShippingGetOrderList($data = [])
{
try {
$params = [
'pay_time_range' => [
'begin_time' => $data[ 'begin_time' ] ?? 0, // 起始时间时间戳形式不填则视为从0开始。
'end_time' => $data[ 'end_time' ] ?? time() // 结束时间时间戳形式不填则视为32位无符号整型的最大值。
],
'page_size' => $data[ 'page_size' ] ?? 100 // 翻页时使用返回列表的长度默认为100。
];
// 订单状态枚举:(1) 待发货;(2) 已发货;(3) 确认收货;(4) 交易完成;(5) 已退款。
if (!empty($data[ 'order_state' ])) {
$params[ 'order_state' ] = $data[ 'order_state' ];
}
// 翻页时使用,获取第一页时不用传入,如果查询结果中 has_more 字段为 true则传入该次查询结果中返回的 last_index 字段可获取下一页。
if (!empty($data[ 'last_index' ])) {
$params[ 'last_index' ] = $data[ 'last_index' ];
}
$result = $this->app->order_shipping->getOrderList($params);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 查询订单发货状态
* @param array $data
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function orderShippingGetOrder($data = [])
{
try {
$params = [];
// 原支付交易对应的微信订单号
if (!empty($data[ 'transaction_id' ])) {
$params[ 'transaction_id' ] = $data[ 'transaction_id' ];
}
// 支付下单商户的商户号,由微信支付生成并下发
if (!empty($data[ 'merchant_id' ])) {
$params[ 'merchant_id' ] = $data[ 'merchant_id' ];
}
// 商户系统内部订单号,只能是数字、大小写字母`_-*`且在同一个商户号下唯一
if (!empty($data[ 'merchant_trade_no' ])) {
$params[ 'merchant_trade_no' ] = $data[ 'merchant_trade_no' ];
}
// 二级商户号
if (!empty($data[ 'sub_merchant_id' ])) {
$params[ 'sub_merchant_id' ] = $data[ 'sub_merchant_id' ];
}
$result = $this->app->order_shipping->getOrder($params);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result[ 'order' ]);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 消息跳转路径设置接口
* @param $order_id
* @param $order_type
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function orderShippingSetMsgJumpPath($order_id, $order_type)
{
try {
$path = 'pages/order/detail?order_id=' . $order_id; // 物流订单
switch ( $order_type ) {
case 2:
// 门店自提订单
$path = 'pages/order/detail_pickup?order_id=' . $order_id;
break;
case 3:
// 外卖订单
$path = 'pages/order/detail_local_delivery?order_id=' . $order_id;
break;
case 4:
// 虚拟订单
$path = 'pages_tool/order/detail_virtual?order_id=' . $order_id;
break;
}
$result = $this->app->order_shipping->setMsgJumpPath($path);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 订单发货信息录入接口
* @param array $data
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function orderShippingUploadShippingInfo($data = [])
{
try {
// 检测微信小程序是否已开通发货信息管理服务
$is_trade_managed = $this->orderShippingIsTradeManaged();
if (!$is_trade_managed[ 'data' ]) {
return $this->success('', $is_trade_managed[ 'message' ]);
}
$order_model = new Order();
$filed = 'o.order_id,o.site_id,o.order_type,o.out_trade_no,o.pay_type,o.mobile,m.weapp_openid';
$join = [
[ 'member m', 'o.member_id=m.member_id', 'left' ]
];
$order_info = model('order')->getInfo([ [ 'order_id', '=', $data[ 'order_id' ] ] ], $filed, 'o', $join);
if (empty($order_info)) {
return $this->error('', '订单不存在');
}
if ($order_info[ 'pay_type' ] != 'wechatpay') {
return $this->success('', '订单未使用微信支付');
}
$shop_model = new ShopModel();
$shop_info = $shop_model->getShopInfo([ [ 'site_id', '=', $this->site_id ] ], '')[ 'data' ];
// 设置消息跳转路径设置接口
$this->orderShippingSetMsgJumpPath($data[ 'order_id' ], $order_info[ 'order_type' ]);
$pay_config = ( new WechatPayModel() )->getPayConfig($order_info[ 'site_id' ])[ 'data' ][ 'value' ];
$logistics_type = 1; // 物流模式发货方式枚举值1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品虚拟商品例如话费充值点卡等无实体配送形式 4、用户自提
// 订单类型 1物流订单2门店自提订单3外卖订单4虚拟订单
if ($order_info[ 'order_type' ] == 1) {
$logistics_type = 1; // 1、实体物流配送采用快递公司进行实体物流配送形式
} else if ($order_info[ 'order_type' ] == 2) {
$logistics_type = 4; // 用户自提
} else if ($order_info[ 'order_type' ] == 3) {
$logistics_type = 2; // 同城配送
} else if ($order_info[ 'order_type' ] == 4) {
$logistics_type = 3; // 虚拟商品,例如话费充值,点卡等,无实体配送形式
}
$order_goods_field = 'order_goods_id,sku_name,num,delivery_no';
$order_goods_list = $order_model->getOrderGoodsList([
[ 'order_id', '=', $order_info[ 'order_id' ] ]
], $order_goods_field, 'order_goods_id asc')[ 'data' ];
$delivery_mode = 1; // 发货模式发货模式枚举值1、UNIFIED_DELIVERY统一发货2、SPLIT_DELIVERY分拆发货 示例值: UNIFIED_DELIVERY
if ($logistics_type == 1) {
$delivery_mode = count($order_goods_list) == 1 ? 1 : 2;
}
$shipping_list = [];
$first_shipping_info = [];
// 获取物流公司
$delivery_list = [];
if ($logistics_type == 1) {
$delivery_list = $this->orderShippingGetDeliveryList()[ 'data' ];
}
foreach ($order_goods_list as $k => $v) {
// 一笔支付单最多分拆成 10 个包裹
if ($k == 10) {
break;
}
$express_company = ''; // 物流公司编码快递公司ID参见「查询物流公司编码列表」物流快递发货时必填 示例值: DHL 字符字节限制: [1, 128]
if ($logistics_type == 1) {
$info = model('express_delivery_package')->getInfo([
[ 'order_id', '=', $order_info[ 'order_id' ] ],
[ 'order_goods_id_array', 'like', '%' . $v[ 'order_goods_id' ] . '%' ]
], 'express_company_id,express_company_name');
if (!empty($info) && $info[ 'express_company_id' ] && !empty($delivery_list)) {
$index = array_search($info[ 'express_company_name' ], array_column($delivery_list, 'delivery_name'));
if ($index !== false && isset($delivery_list[ $index ])) {
$express_company = $delivery_list[ $index ][ 'delivery_id' ];
}
if (empty($express_company)) {
return $this->error([], '物流公司不支持,请更换其他物流公司');
}
} else {
$logistics_type = 2; // 无需物流的情况,无实体配送形式
$delivery_mode = 1; // 统一发货
}
}
$item = [
'tracking_no' => $v[ 'delivery_no' ], // 物流单号,物流快递发货时必填,示例值: 323244567777 字符字节限制: [1, 128]
'express_company' => $express_company, // 物流公司编码快递公司ID参见「查询物流公司编码列表」物流快递发货时必填 示例值: DHL 字符字节限制: [1, 128]
'item_desc' => str_sub($v[ 'sku_name' ], 90) . '*' . $v[ 'num' ], // 商品信息,例如:微信红包抱枕*1个限120个字以内
'contact' => [
'consignor_contact' => '',
'receiver_contact' => ''
]
];
// 寄件人联系方式寄件人联系方式采用掩码传输最后4位数字不能打掩码 示例值: `189****1234, 021-****1234, ****1234, 0**2-***1234, 0**2-******23-10, ****123-8008` 值限制: 0 ≤ value ≤ 1024
if (!empty($shop_info[ 'mobile' ])) {
$item[ 'contact' ][ 'consignor_contact' ] = substr($shop_info[ 'mobile' ], 0, 3) . '****' . substr($shop_info[ 'mobile' ], 7);
}
// 收件人联系方式收件人联系方式为采用掩码传输最后4位数字不能打掩码 示例值: `189****1234, 021-****1234, ****1234, 0**2-***1234, 0**2-******23-10, ****123-8008` 值限制: 0 ≤ value ≤ 1024
if (!empty($order_info[ 'mobile' ])) {
$item[ 'contact' ][ 'receiver_contact' ] = substr($order_info[ 'mobile' ], 0, 3) . '****' . substr($order_info[ 'mobile' ], 7);
}
$shipping_list[] = $item;
if ($k == 0) {
$first_shipping_info = $item;
$first_shipping_inf[ 'sku_name' ] = $v[ 'sku_name' ] . '*' . $v[ 'num' ];
}
}
// 统一发货,只能有一个物流信息,拼装商品信息
if ($delivery_mode == 1) {
if (count($shipping_list) > 1) {
foreach ($shipping_list as $k => $v) {
if ($k > 0) {
$first_shipping_info[ 'item_desc' ] .= ',' . $v[ 'item_desc' ];
}
}
}
$first_shipping_info[ 'item_desc' ] = str_sub($first_shipping_info[ 'item_desc' ], 90);
}
// 分拆发货模式时必填,用于标识分拆发货模式下是否已全部发货完成,只有全部发货完成的情况下才会向用户推送发货完成通知。示例值: true/false
$is_all_delivered = false;
if ($delivery_mode == 2) {
$is_all_delivered = true;
}
$params = [
'order_key' => [
'order_number_type' => 1, // 订单单号类型用于确认需要上传详情的订单。枚举值1使用下单商户号和商户侧单号枚举值2使用微信支付单号。
'mchid' => $pay_config[ 'mch_id' ], // 支付下单商户的商户号,由微信支付生成并下发。
'out_trade_no' => $order_info[ 'out_trade_no' ] // 商户系统内部订单号,只能是数字、大小写字母`_-*`且在同一个商户号下唯一
],
'logistics_type' => $logistics_type, // 物流模式发货方式枚举值1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品虚拟商品例如话费充值点卡等无实体配送形式 4、用户自提
'delivery_mode' => $delivery_mode, // 发货模式发货模式枚举值1、UNIFIED_DELIVERY统一发货2、SPLIT_DELIVERY分拆发货 示例值: UNIFIED_DELIVERY
// 同城配送没有物流信息,只能传一个订单
'shipping_list' => $delivery_mode == 1 ? [ $first_shipping_info ] : $shipping_list, // 物流信息列表,发货物流单列表,支持统一发货(单个物流单)和分拆发货(多个物流单)两种模式,多重性: [1, 10]
'upload_time' => date("c", time()), // 上传时间,用于标识请求的先后顺序 示例值: `2022-12-15T13:29:35.120+08:00`
'payer' => [
'openid' => $order_info[ 'weapp_openid' ] // 用户标识用户在小程序appid下的唯一标识。 下单前需获取到用户的Openid 示例值: oUpF8uMuAJO_M2pxb1Q9zNjWeS6o 字符字节限制: [1, 128]
],
'is_all_delivered' => $is_all_delivered
];
Log::write('发货信息录入接口(参数):' . json_encode($params));
$result = $this->app->order_shipping->uploadShippingInfo($params);
Log::write('发货信息录入接口(结果):' . json_encode($result));
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
// 排除 支付单不存在错误码10060001
if ($result[ 'errcode' ] != 10060001) {
return $this->error([], $result[ "errmsg" ]);
}
}
return $this->success($result);
} catch (\Exception $e) {
return $this->error([], $e->getMessage() . ",File" . $e->getFile() . "line" . $e->getLine());
}
}
/**
* 发货信息录入接口,自备数据
* @param array $data
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function orderShippingUploadShippingInfoByData($data = [])
{
try {
// 检测微信小程序是否已开通发货信息管理服务
$is_trade_managed = $this->orderShippingIsTradeManaged();
if (!$is_trade_managed[ 'data' ]) {
return $this->success('', $is_trade_managed[ 'message' ]);
}
$pay_config = ( new WechatPayModel() )->getPayConfig($data[ 'site_id' ])[ 'data' ][ 'value' ];
$params = [
'order_key' => [
'order_number_type' => 1, // 订单单号类型用于确认需要上传详情的订单。枚举值1使用下单商户号和商户侧单号枚举值2使用微信支付单号。
'mchid' => $pay_config[ 'mch_id' ], // 支付下单商户的商户号,由微信支付生成并下发。
'out_trade_no' => $data[ 'out_trade_no' ] // 商户系统内部订单号,只能是数字、大小写字母`_-*`且在同一个商户号下唯一
],
'logistics_type' => $data[ 'logistics_type' ], // 物流模式发货方式枚举值1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品虚拟商品例如话费充值点卡等无实体配送形式 4、用户自提
'delivery_mode' => $data[ 'delivery_mode' ], // 发货模式发货模式枚举值1、UNIFIED_DELIVERY统一发货2、SPLIT_DELIVERY分拆发货 示例值: UNIFIED_DELIVERY
// 同城配送没有物流信息,只能传一个订单
'shipping_list' => $data[ 'shipping_list' ], // 物流信息列表,发货物流单列表,支持统一发货(单个物流单)和分拆发货(多个物流单)两种模式,多重性: [1, 10]
'upload_time' => date("c", time()), // 上传时间,用于标识请求的先后顺序 示例值: `2022-12-15T13:29:35.120+08:00`
'payer' => [
'openid' => $data[ 'weapp_openid' ] // 用户标识用户在小程序appid下的唯一标识。 下单前需获取到用户的Openid 示例值: oUpF8uMuAJO_M2pxb1Q9zNjWeS6o 字符字节限制: [1, 128]
],
'is_all_delivered' => $data[ 'is_all_delivered' ] // 分拆发货模式时必填,用于标识分拆发货模式下是否已全部发货完成,只有全部发货完成的情况下才会向用户推送发货完成通知。示例值: true/false
];
Log::write('发货信息录入接口,自备数据(参数):' . json_encode($params));
$result = $this->app->order_shipping->uploadShippingInfo($params);
Log::write('发货信息录入接口,自备数据(结果):' . json_encode($result));
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result);
} catch (\Exception $e) {
return $this->error([], $e->getMessage() . ",File" . $e->getFile() . "line" . $e->getLine());
}
}
/**
* todo【暂时没有用到】发货信息合单录入接口
* @param array $data
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function orderShippingUploadCombinedShippingInfo($data = [])
{
try {
$params = [
'order_key' => [
'order_number_type' => $data[ 'order_number_type' ] ?? 1, // 订单单号类型用于确认需要上传详情的订单。枚举值1使用下单商户号和商户侧单号枚举值2使用微信支付单号。
],
'logistics_type' => $data[ 'logistics_type' ], // 物流模式发货方式枚举值1、实体物流配送采用快递公司进行实体物流配送形式 2、同城配送 3、虚拟商品虚拟商品例如话费充值点卡等无实体配送形式 4、用户自提
'delivery_mode' => $data[ 'delivery_mode' ], // 发货模式发货模式枚举值1、UNIFIED_DELIVERY统一发货2、SPLIT_DELIVERY分拆发货 示例值: UNIFIED_DELIVERY
'sub_orders' => $data[ 'sub_orders' ], // 子单物流详情
'upload_time' => $data[ 'upload_time' ], // 上传时间,用于标识请求的先后顺序 示例值: `2022-12-15T13:29:35.120+08:00`
'payer' => [
'openid' => $data[ 'openid' ] // 用户标识用户在小程序appid下的唯一标识。 下单前需获取到用户的Openid 示例值: oUpF8uMuAJO_M2pxb1Q9zNjWeS6o 字符字节限制: [1, 128]
]
];
// 原支付交易对应的微信订单号
if (!empty($data[ 'transaction_id' ])) {
$params[ 'order_key' ][ 'transaction_id' ] = $data[ 'transaction_id' ];
}
// 支付下单商户的商户号,由微信支付生成并下发。
if (!empty($data[ 'mchid' ])) {
$params[ 'order_key' ][ 'mchid' ] = $data[ 'mchid' ];
}
// 商户系统内部订单号,只能是数字、大小写字母`_-*`且在同一个商户号下唯一
if (!empty($data[ 'out_trade_no' ])) {
$params[ 'order_key' ][ 'out_trade_no' ] = $data[ 'out_trade_no' ];
}
$result = $this->app->order_shipping->uploadCombinedShippingInfo($params);
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
return $this->success($result);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 确认收货提醒接口
* @param array $data
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function orderShippingNotifyConfirmReceive($data = [])
{
try {
// 检测微信小程序是否已开通发货信息管理服务
$is_trade_managed = $this->orderShippingIsTradeManaged();
if (!$is_trade_managed[ 'data' ]) {
return $this->success('', $is_trade_managed[ 'message' ]);
}
$filed = 'o.order_id,o.site_id,o.order_type,o.out_trade_no,o.pay_type,o.mobile,m.weapp_openid';
$join = [
[ 'member m', 'o.member_id=m.member_id', 'left' ]
];
$order_info = model('order')->getInfo([ [ 'order_id', '=', $data[ 'order_id' ] ] ], $filed, 'o', $join);
if (empty($order_info)) {
return $this->error('', '订单不存在');
}
if ($order_info[ 'pay_type' ] != 'wechatpay') {
return $this->success('', '订单未使用微信支付');
}
if ($order_info[ 'order_type' ] != 1) {
return $this->success('', '只有物流订单才能进行提醒');
}
// 设置消息跳转路径设置接口
$this->orderShippingSetMsgJumpPath($data[ 'order_id' ], $order_info[ 'order_type' ]);
$pay_config = ( new WechatPayModel() )->getPayConfig($order_info[ 'site_id' ])[ 'data' ][ 'value' ];
$params = [
'merchant_id' => $pay_config[ 'mch_id' ], // 支付下单商户的商户号,由微信支付生成并下发
'merchant_trade_no' => $order_info[ 'out_trade_no' ], // 商户系统内部订单号只能是数字、大小写字母_-*且在同一个商户号下唯一
'received_time' => time() // 快递签收时间,时间戳形式
];
Log::write('确认收货提醒接口(参数):' . json_encode($params));
$result = $this->app->order_shipping->notifyConfirmReceive($params);
Log::write('确认收货提醒接口(结果):' . json_encode($result));
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
Log::write('微信小程序确认收货提醒接口报错:' . json_encode($result));
return $this->success([], $result[ "errmsg" ]);
}
return $this->success($result);
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
/**
* 获取物流公司运力id列表get_delivery_list
* @param array $data
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function orderShippingGetDeliveryList()
{
$cache = Cache::get('orderShippingGetDeliveryList');
if ($cache) return $cache;
try {
$result = $this->app->order_shipping->getDeliveryList();
if (isset($result[ 'errcode' ]) && $result[ 'errcode' ] != 0) {
return $this->error([], $result[ "errmsg" ]);
}
$data = $this->success($result[ 'delivery_list' ]);
Cache::set('orderShippingGetDeliveryList', $data);
return $data;
} catch (\Exception $e) {
return $this->error([], $e->getMessage());
}
}
}