作者 竞泽

add:all

/vendor/
... ...
{
"name": "lackoxygen/customs",
"type": "library",
"description": "海关总署179",
"license": "MIT",
"authors": [
{
"name": "jz",
"email": "jingzeou@outlook.com"
}
],
"require": {
"php": ">=7.1",
"illuminate/support": "^5.8"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
},
"autoload": {
"psr-4": {
"Lackoxygen\\Customs\\": "src/"
}
},
"extra": {
"laravel": {
"providers": [
"Lackoxygen\\Customs\\CustomsProvider"
]
}
}
}
... ...
<?php
return [
'host' => 'https://swapptest.singlewindow.cn',
'cert_no' => '015ffaef',
'client' => [
'timeout' => 60
]
];
... ...
<?php
namespace Lackoxygen\Customs;
use GuzzleHttp\Client as GuzzleHttp;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\RequestOptions;
use Lackoxygen\Customs\Contract\RequestInterface;
use Lackoxygen\Customs\Exception\Exception;
use Psr\Http\Message\ResponseInterface;
class Client
{
/**
* @var GuzzleHttp
*/
protected $guzzleHttp;
/**
* @var RequestInterface
*/
protected $request;
/**
* Client constructor.
*/
protected function __construct(RequestInterface $request)
{
$clientConfig = (array)config('customs.client', [
'timeout' => 30
]);
$config = $clientConfig + [
'base_uri' => config('customs.host')
];
$this->guzzleHttp = new GuzzleHttp($config);
$this->request = $request;
}
/**
* @return ResponseInterface
* @throws GuzzleException
*/
protected function send(): ResponseInterface
{
return $this->guzzleHttp->request($this->request->getMethod(), $this->request->getPath(), $this->getOptions());
}
/**
* @return array
*/
protected function getOptions(): array
{
$requestArray = ['payExInfoStr' => $this->request->toJson(JSON_UNESCAPED_UNICODE)];
$options = [];
if ($this->request->getMethod() === "GET") {
$options[RequestOptions::QUERY] = $requestArray;
} elseif ($this->request->getContentType() === 'application/x-www-form-urlencoded') {
$options[RequestOptions::FORM_PARAMS] = $requestArray;
$options[RequestOptions::HEADERS] = ['Content-Type' => 'application/x-www-form-urlencoded'];
} elseif (strpos($this->request->getContentType(), 'application/json') !== false) {
$options[RequestOptions::JSON] = $requestArray;
$options[RequestOptions::HEADERS] = ['Content-Type' => 'application/json'];
}
return $options;
}
/**
* @param RequestInterface $request
*
* @return string
* @throws Exception
*/
public static function request(RequestInterface $request): string
{
$client = new static($request);
try {
$response = $client->send();
} catch (\Throwable $e) {
throw new Exception($e);
}
$content = $response->getBody()->getContents();
$response->getBody()->rewind();
return $content;
}
}
... ...
<?php
namespace Lackoxygen\Customs\Contract;
interface RequestInterface
{
/**
* @return string
*/
public function getPath(): string;
/**
* @return string
*/
public function getMethod(): string;
/**
* @return string
*/
public function getContentType(): string;
/**
* @return array
*/
public function toArray(): array;
/**
* @return string
*/
public function toJson($options = 0): string;
}
... ...
<?php
namespace Lackoxygen\Customs;
use Lackoxygen\Customs\Request\PayExInfo;
use Lackoxygen\Customs\Utils\Trigger;
class Customs
{
/**
* @param callable $func
*
* @return string
* @throws Exception\Exception
*/
public function report(callable $func)
{
$payExInfo = PayExInfo::make();
Trigger::closure($func, $payExInfo);
return Client::request($payExInfo);
}
}
... ...
<?php
namespace Lackoxygen\Customs;
use Illuminate\Support\ServiceProvider;
class CustomsProvider extends ServiceProvider
{
/**
* @var bool
*/
protected $defer = true;
/**
* @var array
*/
protected $commands = [];
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->commands($this->commands);
}
public function boot()
{
$this->publishes([__DIR__ . '/../config/customs.php' => config_path('customs.php')]);
}
/**
* @return string[]
*/
public function provides()
{
return ['customs', Customs::class];
}
}
... ...
<?php
namespace Lackoxygen\Customs\Exception;
class Exception extends \Exception
{
}
... ...
<?php
namespace Lackoxygen\Customs\Facade;
use Illuminate\Support\Facades\Facade;
use Lackoxygen\Customs\Customs;
/**
* Class CustomsFacade
*
* @method static report(callable $func)
*/
class CustomsFacade extends Facade
{
/**
* @return string
*/
protected static function getFacadeAccessor(): string
{
return Customs::class;
}
}
... ...
<?php
namespace Lackoxygen\Customs\Request;
use Lackoxygen\Customs\Request\Struct\PayExchangeInfoHead;
use Lackoxygen\Customs\Request\Struct\PayExchangeInfoItem;
class PayExInfo extends Request
{
/**
* @return mixed
*/
public function getSessionID()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 海关发起请求时,平台接收的会话ID。
*/
public function setSessionID(): void
{
$this->mSet(__FUNCTION__, func_get_arg(0));
}
/**
* @return mixed
*/
public function getPayExchangeInfoHead()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param mixed $payExchangeInfoHead 支付原始数据表头
*/
public function setPayExchangeInfoHead(PayExchangeInfoHead $payExchangeInfoHead): void
{
$this->mSet(__FUNCTION__, $payExchangeInfoHead);
}
/**
* @return mixed
*/
public function getPayExchangeInfoLists()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param PayExchangeInfoItem $payExchangeInfoItem 支付原始数据表体
*/
public function pushPayExchangeInfoLists(PayExchangeInfoItem $payExchangeInfoItem): void
{
$this->mPush(__FUNCTION__, $payExchangeInfoItem);
}
/**
* @return mixed
*/
public function getServiceTime()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 调用时的系统时间
*/
public function setServiceTime(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getCertNo()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 证书编号
*/
public function setCertNo(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getSignValue()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 签名结果值
*/
public function setSignValue(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
}
... ...
<?php
namespace Lackoxygen\Customs\Request;
use Lackoxygen\Customs\Contract\RequestInterface;
use Lackoxygen\Customs\Utils\Collection;
class Request extends Collection implements RequestInterface
{
/**
* @var string
*/
protected $path = '/ceb2grab/grab/realTimeDataUpload';
/**
* @var string
*/
protected $method = 'POST';
/**
* @var string
*/
protected $contentType = 'application/x-www-form-urlencoded';
/**
* @inheritDoc
*/
public function getPath(): string
{
return $this->path;
}
/**
* @inheritDoc
*/
public function getMethod(): string
{
return $this->method;
}
/**
* @inheritDoc
*/
public function getContentType(): string
{
return $this->contentType;
}
/**
* @inheritDoc
*/
public function toArray(): array
{
return parent::toArray();
}
/**
* @inheritDoc
*/
public function toJson($options = 0): string
{
return parent::toJson($options);
}
}
... ...
<?php
namespace Lackoxygen\Customs\Request\Struct;
use Lackoxygen\Customs\Utils\Collection;
class GoodsInfoItem extends Collection
{
/**
* @return string
*/
public function getGname()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 商品名称
*/
public function setGname(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getItemLink()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 商品展示链接地址
*/
public function setItemLink(): void
{
$itemLink = func_get_arg(0);
if (!empty($itemLink)) {
$itemLink = urlencode($itemLink);
}
$this->mSet(__FUNCTION__, (string)$itemLink);
}
}
... ...
<?php
namespace Lackoxygen\Customs\Request\Struct;
use Lackoxygen\Customs\Utils\Collection;
class PayExchangeInfoHead extends Collection
{
/**
* @return mixed
*/
public function getGuid()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 企业系统生成36位唯一序号(英文字母大写)
*/
public function setGuid(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getInitalRequest()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 跨境电商平台企业向支付企业发送的原始信息
*/
public function setInitalRequest(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getInitalResponse()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 支付企业向跨境电商平台企业反馈的原始信息
*/
public function setInitalResponse(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getEbpCode()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 电商平台的海关注册登记编号
*/
public function setEbpCode(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getPayCode()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 支付企业的海关注册登记编号
*/
public function setPayCode(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getPayTransactionId()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 交易唯一编号(可在央行认可的机构验证)
*/
public function setPayTransactionId(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getTotalAmount()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param float 实际交易金额
*/
public function setTotalAmount(): void
{
$this->mSet(__FUNCTION__, func_get_arg(0));
}
/**
* @return mixed
*/
public function getCurrency()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 实际交易币制(海关编码
*/
public function setCurrency(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getVerDept()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 1-银联 2-网联 3-其他
*/
public function setVerDept(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getPayType()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 用户支付的类型。1-APP 2-PC 3-扫码 4-其他
*/
public function setPayType(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getTradingTime()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 交易支付时间
*/
public function setTradingTime(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getNote()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 备注
*/
public function setNote(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
}
... ...
<?php
namespace Lackoxygen\Customs\Request\Struct;
use Lackoxygen\Customs\Utils\Collection;
class PayExchangeInfoItem extends Collection
{
/**
* @return mixed
*/
public function getOrderNo()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 交易平台向海关申报订单的的订单编号。需返回原始请求内的所有订单
*/
public function setOrderNo(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getGoodsInfo()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param GoodsInfoItem $goodsInfoItem
*/
public function pushGoodsInfo(GoodsInfoItem $goodsInfoItem): void
{
$this->mPush(__FUNCTION__, $goodsInfoItem);
}
/**
* @return mixed
*/
public function getRecpAccount()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 交易商品的卖方商户账号。电商平台自营商户应填写自营商户的收款账户;非自营电商应填写非自营商户的收款账户
*/
public function setRecpAccount(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getRecpCode()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 应填写收款企业代码(境内企业为统一社会信用代码;境外企业可不填写)
*/
public function setRecpCode(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
/**
* @return mixed
*/
public function getRecpName()
{
return $this->mGet(__FUNCTION__);
}
/**
* @param string 应填写收款企业名称
*/
public function setRecpName(): void
{
$this->mSet(__FUNCTION__, (string)func_get_arg(0));
}
}
... ...
<?php
namespace Lackoxygen\Customs\Utils;
use Illuminate\Support\Collection as BaseCollection;
class Collection extends BaseCollection
{
/**
* @param $method
* @param $value
*/
public function mSet($method, $value)
{
parent::offsetSet($this->cutKey($method, 3), $value);
}
/**
* @param $method
* @param $value
*/
public function mPush($method, $value)
{
$key = $this->cutKey($method, 4);
$listCollection = $this->get($key);
if (!$listCollection instanceof BaseCollection) {
$listCollection = BaseCollection::make();
}
$listCollection->push($value);
$this->offsetSet($key, $listCollection);
}
/**
* @param $method
* @param null $default
*
* @return mixed
*/
public function mGet($method, $default = null)
{
return parent::get($this->cutKey($method, 3), $default);
}
/**
* @param string $key
* @param int $offset
*
* @return string
*/
public function cutKey(string $key, int $offset = 0): string
{
return lcfirst(substr($key, $offset));
}
}
... ...
<?php
namespace Lackoxygen\Customs\Utils;
use Lackoxygen\Customs\Request\PayExInfo;
class Signature
{
/**
* @var PayExInfo $exInfo
*/
protected $exInfo;
/**
* Signature constructor.
*
* @param PayExInfo $exInfo
*/
public function __construct(PayExInfo $exInfo)
{
$this->exInfo = $exInfo;
}
/**
* @return string
*/
public function signatureString(): string
{
$buffer = "\"sessionID\":\"{$this->exInfo->getSessionID()}\"";
$buffer .= "||";
$buffer .= "\"payExchangeInfoHead\":\"{$this->exInfo->getPayExchangeInfoHead()->toJson(JSON_UNESCAPED_UNICODE)}\"";
$buffer .= "||";
$buffer .= "\"payExchangeInfoLists\":\"{$this->exInfo->getPayExchangeInfoLists()->toJson(JSON_UNESCAPED_UNICODE)}\"";
$buffer .= "||";
$buffer .= "\"serviceTime\":\"{$this->exInfo->getServiceTime()}\"";
return $buffer;
}
}
... ...
<?php
namespace Lackoxygen\Customs\Utils;
class Trigger
{
/**
* @param $func
* @param ...$argv
*
* @return false|mixed|null
*/
public static function closure($func, ...$argv)
{
if (!$func instanceof \Closure) {
return null;
}
return call_user_func($func, ...$argv);
}
}
... ...
<?php
use Carbon\Carbon;
use Illuminate\Support\Str;
use Lackoxygen\Customs\Facade\CustomsFacade;
use Lackoxygen\Customs\Request\PayExInfo;
use Lackoxygen\Customs\Request\Struct\GoodsInfoItem;
use Lackoxygen\Customs\Request\Struct\PayExchangeInfoHead;
use Lackoxygen\Customs\Request\Struct\PayExchangeInfoItem;
foreach (['./', '../', '../../', '../../../'] as $pathPrefix) {
$loaderFile = $pathPrefix . 'vendor/autoload.php';
if (file_exists($loaderFile)) {
require_once $loaderFile;
break;
}
}
$response = CustomsFacade::report(function (PayExInfo $exInfo) {
$collection = $exInfo;
$payExchangeInfoItem = PayExchangeInfoItem::make();
$guid = Str::uuid()->toString();
$tradingTime = date('YmdHis');
$serviceTime = Carbon::now()->getPreciseTimestamp(3);
$uuid = "8D5925BF-4DC7-4CC5-AE0B-AB9AA0800E5C";
$sid = '4BE53EF8-2432-432C-A102-D33835D2C153';
$tradingTime = "20210716101722";
$serviceTime = "1626401880241";
$collection->setSessionID($sid);
$payExchangeInfoHead = PayExchangeInfoHead::make();
$payExchangeInfoHead->setGuid($uuid);
$payExchangeInfoHead->setInitalRequest('原始请求');//支付原始请求
$payExchangeInfoHead->setInitalResponse('ok');//支付原始返回
$payExchangeInfoHead->setEbpCode('4423968643');
$payExchangeInfoHead->setPayCode('312226T001');
$payExchangeInfoHead->setPayTransactionId('b201907311451410140026320');
$payExchangeInfoHead->setTotalAmount(1);
$payExchangeInfoHead->setCurrency(502);
$payExchangeInfoHead->setVerDept(3);
$payExchangeInfoHead->setPayType(1);
$payExchangeInfoHead->setTradingTime($tradingTime);
$payExchangeInfoHead->setNote('测试订单');
$collection->setPayExchangeInfoHead($payExchangeInfoHead);
$goodsItem1 = GoodsInfoItem::make();
$goodsItem1->setGname('测试商品01');
$goodsItem1->setItemLink('https://test.m.vodeshop.com/o2o/pages/seckill/goodsDetail?activityGoodsId=261&retail_goods_id=21&groupType=3');
$goodsItem2 = GoodsInfoItem::make();
$goodsItem2->setGname('测试商品2');
$goodsItem2->setItemLink('https://test.m.vodeshop.com/o2o/pages/seckill/goodsDetail?activityGoodsId=441&retail_goods_id=34&groupType=3');
$payExchangeInfoItem->setOrderNo('RE202102252327330035730408');
$payExchangeInfoItem->pushGoodsInfo($goodsItem1);
$payExchangeInfoItem->pushGoodsInfo($goodsItem2);
$payExchangeInfoItem->setRecpAccount('OSA571908863132601');
$payExchangeInfoItem->setRecpCode('91440113304476710E');
$payExchangeInfoItem->setRecpName('guangdongloveback');
$collection->pushPayExchangeInfoLists($payExchangeInfoItem);
$collection->setServiceTime($serviceTime);
$collection->setCertNo(config('customs.cert_no'));
$collection->setSignValue("n48bqbODd6cNU/RDErvHHb/4+65ePk1MTxVL5Kln+dwM7K2f/BLvCv0OwUN2j7qeFvvJFTfTfpDzb2xc+OwGc/U0ZTWby9BDV47eXQE+o6JoNtXJ5yR6+lje620tW987Pzd4kO9+gwmNKJoufwwaCUTEm6tGNlzaYliOpxaiYS4=");
});
var_dump($response);
... ...