'', 'client_id' => '', 'access_token' => '', // 非必填,通过code获取的access_token 'timestamp' => '', 'data_type' => '', // 非必填,响应格式,即返回数据的格式,JSON或者XML(二选一),默认JSON,注意是大写 'version' => '', // 非必填, API协议版本号,默认为V1,可不填 'sign' => '' ]; public function auth($type = 'ktt') { $accessToken = $this->getAccessTokenWithCode(); $accessToken['scope'] = json_encode($accessToken['scope'], 256); $accessToken['status'] = Shop::$STATUS_AUTHORIZED; if ('ktt' === $type) { $accessToken['pop_auth_token_create_response'] = json_encode($accessToken, 256); $this->shop->update($accessToken); } if ('ship' === $type) { unset($accessToken['r1_expires_at'], $accessToken['r1_expires_in'], $accessToken['r2_expires_at'], $accessToken['r2_expires_in'], $accessToken['w1_expires_at'], $accessToken['w1_expires_in'], $accessToken['w2_expires_at'], $accessToken['w2_expires_in'], $accessToken['request_id']); ShopShip::query()->updateOrCreate( ['shop_id' => $this->shop->id], $accessToken ); } return $this->shop; } public function downloadGoodsListAndBind($activityNo, $title = '', $page = 1) { [$type, $appendParams] = Goods::downloadGoods($activityNo, $page); $res = $this->doRequest($type, $appendParams); $goods = $res['ktt_goods_query_list_response']['goods_list']; $this->bindGoods($goods, $title); $pageNum = ceil($res['ktt_goods_query_list_response']['total'] / $appendParams['size']); if ($pageNum > $page && 60 >= $page) { $this->downloadGoodsListAndBind($activityNo, $title, $page + 1); } } public function bindGoods($goods, $title = '') { Goods::bindGoods($goods, $this->shop->id, $title); } public function incrQuantity($businessGoodsSku, $num, $incremental) { [$type, $appendParams] = Goods::incrQuantity($businessGoodsSku['goods_id'], $businessGoodsSku['sku_id'], $num, $incremental ? 1 : 2); $this->doRequest($type, $appendParams); } public function batchIncrQuantity($businessGoodsSkus, $num, $incremental) { $batchAppendParams = []; $logData = []; foreach ($businessGoodsSkus as $businessGoodsSku) { [$type, $appendParams] = Goods::incrQuantity($businessGoodsSku['goods_id'], $businessGoodsSku['sku_id'], $num, $incremental ? 1 : 2); $logData[] = $appendParams; $appendParams['type'] = $type; $appendParams['client_id'] = $this->clientId; $appendParams['timestamp'] = time(); $appendParams['access_token'] = $this->getShop()->access_token; $appendParams['sign'] = $this->getSign($appendParams); $batchAppendParams[] = $appendParams; } $this->batchAsyncPostRequest('https://gw-api.pinduoduo.com/api/router', $batchAppendParams); if ($logData) { Log::info('本次批量同步: ' . json_encode($logData, 256)); } } /** * 下载订单 * * @param $beginTime * @param $endTime * @param int $page * @param string $downloadType * @return void */ public function downloadOrdersAndSave($beginTime, $endTime, $downloadType = 'default', $page = 1) { if ('increment' === $downloadType) { [$type, $appendParams] = Order::downloadIncrementOrders($beginTime, $endTime, $page); $responseName = 'ktt_increment_order_query_response'; } else { [$type, $appendParams] = Order::downloadOrders($beginTime, $endTime, $page); $responseName = 'ktt_order_list_response'; } $res = $this->doRequest($type, $appendParams); if (!isset($res[$responseName])) { return; } $this->saveOrders($res[$responseName]['order_list']); $pageNum = ceil($res[$responseName]['total_count'] / $appendParams['page_size']); if ($pageNum > $page && 30 >= $page) { $this->downloadOrdersAndSave($beginTime, $endTime, $downloadType, $page + 1); } } public function downloadAfterSaleOrdersAndSave($beginTime, $endTime, $page = 1) { [$type, $appendParams] = Order::downloadIncrementAfterSaleOrders($beginTime, $endTime, $page); $responseName = 'ktt_after_sales_incermet_list_response'; $res = $this->doRequest($type, $appendParams); if (!isset($res[$responseName])) { return; } $this->saveAfterSaleOrdersServer($res[$responseName]['list']); if ($res[$responseName]['has_next'] == false) { return; } $pageNum = ceil($res[$responseName]['total_count'] / $appendParams['page_size']); if ($pageNum > $page && 30 >= $page) { $this->downloadAfterSaleOrdersAndSave($beginTime, $endTime, $page + 1); } } public function saveAfterSaleOrdersServer($orders) { $shopId = $this->getShop()->id; $now = Carbon::now()->toDateTimeString(); foreach ($orders as $k => $v) { $model = BusinessAfterSaleOrder::query()->where("shop_id", "=", $shopId) ->where("after_sales_biz_sn", "=", $v['after_sales_biz_sn'])->first(); if (empty($model)) { $model = new BusinessAfterSaleOrder(); } else { $model->updated_at = $now; } $model->shop_id = $shopId; $model->refund_amount = $v['apply_extension']['refund_amount']; $model->refund_shipping_amount = $v['apply_extension']['refund_shipping_amount']; $model->description = $v['apply_extension']['description'] ?? ''; $model->reason = $v['apply_extension']['reason'] ?? ''; $model->sub_extensions = isset($v['apply_extension']['sub_extensions']) ? json_encode($v['apply_extension']['sub_extensions']) : null; $model->return_goods_extension = isset($v['return_goods_extension']) ? json_encode($v['return_goods_extension']) : null; $model->image_list = isset($v['apply_extension']['image_list']) ? json_encode($v['apply_extension']['image_list']) : null; $model->apply_type = $v['apply_type']; $model->order_sn = $v['order_sn']; $model->after_sales_biz_sn = $v['after_sales_biz_sn']; $model->after_sales_status = $v['after_sales_status']; $model->after_sale_created_at = !empty($v['created_at']) ? Carbon::createFromTimestamp($v['created_at'] / 1000)->toDateTimeString() : ""; $model->after_sale_updated_at = !empty($v['updated_at']) ? Carbon::createFromTimestamp($v['created_at'] / 1000)->toDateTimeString() : ""; $model->save(); } } public function queryStatusAndSync($order) { [$type, $appendParams] = Order::getOrderInfo($order->order_sn); $responseName = 'ktt_order_get_response'; $res = $this->doRequest($type, $appendParams); if (!isset($res[$responseName]['order_info'])) { return; } $queryOrder = $res[$responseName]['order_info']; $needUpdate = false; if ($order->shipping_status != $queryOrder['shipping_status']) { $order->shipping_status = $queryOrder['shipping_status']; $needUpdate = true; } if ($order->cancel_status != $queryOrder['cancel_status']) { $order->cancel_status = $queryOrder['cancel_status']; $needUpdate = true; } if ($order->after_sales_status!= $queryOrder['cancel_status']) { //售后状态更新 $order->after_sales_status = $queryOrder['after_sales_status']; $needUpdate = true; } if ($needUpdate) { $goodsSkuNum = 0; foreach ($queryOrder['sub_order_list'] as $item) { $orderItem = BusinessOrderItem::firstOrNew(['shop_id' => $order->shop_id, 'business_order_id' => $order->id, 'goods_id' => $item['goods_id'], 'sku_id' => $item['sku_id']], $item); if ($item['external_sku_id']) { $goodsSku = GoodsSku::query() ->with('combinationGoods') ->where('external_sku_id', $item['external_sku_id']) ->first('external_sku_id'); $combinationNum = $goodsSku ? ($goodsSku->combinationGoods->count() ?: 1) : 1; $goodsSkuNum += $combinationNum; } else { $goodsSkuNum++; } if (!empty($orderItem->id)) { $orderItem->update($item); } } $order->goods_sku_num = $goodsSkuNum; $order->save(); } } public function getOrderInfo($orderSn) { [$type, $appendParams] = Order::getOrderInfo($orderSn); return $this->doRequest($type, $appendParams); } protected function getAccessTokenWithCode() { $type = 'pdd.pop.auth.token.create'; $res = $this->doRequest($type, ['code' => $this->code]); return $res['pop_auth_token_create_response']; } protected function getSign($params) { ksort($params); $str = ''; foreach ($params as $key => $val) { $str .= $key . $val; } $str = $this->clientSecret . $str . $this->clientSecret; return strtoupper(md5($str)); } public function doRequest($type, $appendParams = [], $url = 'https://gw-api.pinduoduo.com/api/router') { $publicParams = [ 'type' => $type, 'client_id' => $this->clientId, 'timestamp' => time() ]; if ('pdd.pop.auth.token.create' !== $type) { $publicParams['access_token'] = $this->getShop()->access_token; } $publicParams = array_merge($publicParams, $appendParams); $publicParams['sign'] = $this->getSign($publicParams); $res = $this->formDataPostRequest($url, $publicParams); Log::info("快团团请求", ["param" => $publicParams, "res" => $res]); if (isset($res['error_response'])) { // "error_msg":"业务服务错误","sub_msg":"该店铺下不存在该商品","sub_code":"11","error_code":50001 // "error_msg":"业务服务错误","sub_msg":"该SKU在快团团中设置的库存为无限库存,不支持修改库存","sub_code":"13","error_code":50001 if (50001 == $res['error_response']['error_code'] && in_array($res['error_response']['sub_code'], ['13', '11'])) { BusinessGoodsSku::query()->where('goods_id', $appendParams['goods_id'])->where('sku_id', $appendParams['sku_id'])->update(['is_sync' => 0]); } Log::error(json_encode($res, 256) . $type); } return $res; } public function downloadGoods($skuId) { $goodsSku = GoodsSku::query() ->with(['goods:id,goods_code']) ->find($skuId); $code = $goodsSku->goods->goods_code . '_' . $goodsSku->sku_code; $business = BusinessGoodsSku::query()->where('shop_id', $this->shop->id)->where('external_sku_id', $code)->first(['goods_id', 'sku_id']); [$type, $appendParams] = Goods::downloadSingle($business->goods_id); $res = $this->doRequest($type, $appendParams); $goods = $res['response']['result']; $this->bindGoods([$goods]); } public function getAuthUrl($shopId, $platId) { $state = $shopId . '_' . $platId; return "https://oauth.pinduoduo.com/authorize/ktt?client_id={$this->clientId}&redirect_uri={$this->redirectUri}&state={$state}"; } public function createSku() { [$type, $appendParams] = Goods::createSpec(); return $this->doRequest($type, $appendParams); } public function createGroup($localGroupId) { [$type, $appendParams] = Groups::createGroup($localGroupId, $this->shop); $res = $this->doRequest($type, $appendParams); if (isset($res['response']['success'])) { $group = GroupsModel::query()->where('parent_id', $localGroupId)->where('shop_id', $this->shop->id)->first(); $group->activity_no = $res['response']['activity_no']; $group->save(); } return $res; } public function queryGroupStatus($localGroupId) { [$type, $appendParams] = Groups::queryGroupStatus($localGroupId, $this->shop->id); $res = $this->doRequest($type, $appendParams); if (isset($res['response'])) { $group = GroupsModel::query()->where('parent_id', $localGroupId)->where('shop_id', $this->shop->id)->first(); $group->create_status = $res['response']['status']; if (1 === $res['response']['status']) { $group->qr_code_url = $res['response']['qr_code_url']; // foreach ($res['response']['goods_list'] as $goods) { // $groupGoods = GroupGoods::query() // ->where('group_id', $localGroupId) // ->where('external_sku_id', $goods['sku_list'][0]['external_sku_id']) // ->first(); // $groupGoods->ktt_goods_id = $goods['goods_id']; // $groupGoods->ktt_sku_id = $goods['sku_list'][0]['sku_id']; // $groupGoods->save(); // } } if (2 === $res['response']['status']) { $group->error_msg = $res['response']['error_msg']; } $group->save(); } return $res; } public function queryGroup() { [$type, $appendParams] = Groups::queryGroup(); $res = $this->doRequest($type, $appendParams); // if (isset($res['ktt_group_query_list_response'])) { // foreach ($res['ktt_group_query_list_response']['activity_list'] as $activity) { // $group = GroupsModel::query()->where('activity_no', $activity['activity_no'])->first(); // if ($group) { // $group->is_help_sell = $activity['']; // $group->status = $activity['']; // $group->save(); // } // } // } return $res; } public function uploadImage($url) { [$type, $appendParams] = Goods::uploadImage($url); return $this->doRequest($type, $appendParams); } public function createLogistic($orderSn, $waybillNo) { [$type, $appendParams] = Order::createOrderLogistic($orderSn, $waybillNo); return $this->doRequest($type, $appendParams); } public function cancelLogistic($orderSn, $waybillNo) { [$type, $appendParams] = Order::deleteOrderLogistic($orderSn, $waybillNo); return $this->doRequest($type, $appendParams); } public function getLogisticsCompanies() { [$type, $appendParams] = Order::getLogisticsCompanies(); return $this->doRequest($type, $appendParams); } }