├── TODO.md
├── .gitignore
├── Info
├── InfoDecorator.php
├── Period
│ ├── Decorator
│ │ ├── BackUrl.php
│ │ ├── NotifyUrl.php
│ │ ├── ReturnUrl.php
│ │ ├── Webhook.php
│ │ └── Memo.php
│ ├── BasicInfo.php
│ └── Info.php
├── Decorator
│ ├── EnableWebAtm.php
│ ├── EnableLinePay.php
│ ├── EnableUnionPay.php
│ ├── EnableGooglePay.php
│ ├── EnableCreditBonus.php
│ ├── EnableSamsungPay.php
│ ├── EnableAmericanExpress.php
│ ├── EnableAliPay.php
│ ├── EnableEzPay.php
│ ├── EnableVacc.php
│ ├── EnableCvs.php
│ ├── EnableBarcode.php
│ ├── Enable.php
│ ├── PayerEmailEditable.php
│ ├── Language.php
│ ├── PayInInstallments.php
│ ├── PayComplete.php
│ ├── PayCancel.php
│ ├── EnableCredit.php
│ ├── Comment.php
│ ├── TradeLimit.php
│ ├── EnableCvsCom.php
│ ├── AliPayProduct.php
│ └── OfflinePay.php
├── AliPayInfo.php
├── BasicInfo.php
├── AliPayBasicInfo.php
└── Info.php
├── Constants
├── Cipher.php
├── LanguageType.php
└── Period
│ ├── VersionType.php
│ ├── AlterType.php
│ └── PeriodStartType.php
├── Contracts
├── InfoInterface.php
├── PayerInterface.php
├── AliPayPayerInterface.php
├── QuickCreditInterface.php
├── OrderInterface.php
├── AliPayProductInterface.php
└── Period
│ ├── ContactInterface.php
│ └── OrderInterface.php
├── Exceptions
└── TradeInfoException.php
├── composer.json
├── phpunit.xml.dist
├── Tests
├── MerchantTest.php
├── InfoTest.php
├── NewebPayTest.php
└── AliPayInfoTest.php
├── LICENSE
├── Merchant.php
├── Cryption.php
├── Response.php
├── README.md
└── NewebPay.php
/TODO.md:
--------------------------------------------------------------------------------
1 | # TODO
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.lock
3 | phpunit.xml
--------------------------------------------------------------------------------
/Info/InfoDecorator.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
10 | [
11 | 'BackURL' => $this->url,
12 | ];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableWebAtm.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
10 | [
11 | 'WEBATM' => $this->isEnable? 1: 0,
12 | ];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Info/Period/Decorator/NotifyUrl.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
10 | [
11 | 'NotifyURL' => $this->url,
12 | ];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Info/Period/Decorator/ReturnUrl.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
10 | [
11 | 'ReturnURL' => $this->url,
12 | ];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableLinePay.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
10 | [
11 | 'LINEPAY' => $this->isEnable? 1: 0,
12 | ];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableUnionPay.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
10 | [
11 | 'UNIONPAY' => $this->isEnable? 1: 0,
12 | ];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableGooglePay.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
10 | [
11 | 'ANDROIDPAY' => $this->isEnable? 1: 0,
12 | ];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableCreditBonus.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
10 | [
11 | 'CreditRed' => $this->isEnable? 1: 0,
12 | ];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableSamsungPay.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
10 | [
11 | 'SAMSUNGPAY' => $this->isEnable? 1: 0,
12 | ];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableAmericanExpress.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
10 | [
11 | 'CREDITAE' => $this->isEnable? 1: 0,
12 | ];
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Contracts/PayerInterface.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
14 | [
15 | 'ALIPAY' => $this->isEnable? 1: 0,
16 | ];
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableEzPay.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
14 | [
15 | 'P2G' => $this->isEnable? 1: 0,
16 | ];
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableVacc.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
15 | [
16 | 'VACC' => $this->isEnable? 1: 0,
17 | ];
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Constants/Period/AlterType.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
15 | [
16 | 'CVS' => $this->isEnable? 1: 0,
17 | ];
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableBarcode.php:
--------------------------------------------------------------------------------
1 | info->getInfo() +
15 | [
16 | 'BARCODE' => $this->isEnable? 1: 0,
17 | ];
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Contracts/AliPayPayerInterface.php:
--------------------------------------------------------------------------------
1 | info = $info;
22 |
23 | $this->url = $url;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Contracts/OrderInterface.php:
--------------------------------------------------------------------------------
1 | info = $info;
19 |
20 | $this->isEnable = $isEnable;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Info/AliPayInfo.php:
--------------------------------------------------------------------------------
1 | count = $count;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Contracts/AliPayProductInterface.php:
--------------------------------------------------------------------------------
1 | info = $info;
19 |
20 | $this->canEditEmail = $canEditEmail;
21 | }
22 |
23 | public function getInfo()
24 | {
25 | return $this->info->getInfo() +
26 | [
27 | 'EmailModify' => $this->canEditEmail? 1: 0,
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Info/BasicInfo.php:
--------------------------------------------------------------------------------
1 | $this->merchantId,
13 | 'RespondType' => 'JSON',
14 | 'TimeStamp' => time(),
15 | 'Version' => NewebPay::VERSION,
16 | 'NotifyURL' => $this->notifyUrl,
17 | 'Amt' => $this->order->getAmt(),
18 | 'ItemDesc' => $this->order->getItemDesc(),
19 | 'MerchantOrderNo' => $this->order->getMerchantOrderNo(),
20 | 'Email' => $this->payer->getEmail(),
21 | 'LoginType' => $this->payer->getLoginType()? 1: 0,
22 | ];
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Info/Decorator/Language.php:
--------------------------------------------------------------------------------
1 | info = $info;
20 |
21 | $this->language = $language;
22 | }
23 |
24 | public function getInfo()
25 | {
26 | return $this->info->getInfo() +
27 | [
28 | 'LangType' => $this->language ?? LanguageType::ZH_TW,
29 | ];
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Constants/Period/PeriodStartType.php:
--------------------------------------------------------------------------------
1 | info = $info;
25 |
26 | $this->instFlag = $instFlag;
27 | }
28 |
29 | public function getInfo()
30 | {
31 | return $this->info->getInfo() +
32 | [
33 | 'InstFlag' => $this->instFlag,
34 | ];
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Info/Decorator/PayComplete.php:
--------------------------------------------------------------------------------
1 | info = $info;
23 |
24 | $this->setReturnUrl($returnUrl);
25 | }
26 |
27 | public function getInfo()
28 | {
29 | return $this->info->getInfo() +
30 | [
31 | 'ReturnURL' => $this->returnUrl,
32 | ];
33 | }
34 |
35 | protected function setReturnUrl(string $returnUrl = null)
36 | {
37 | $this->returnUrl = $returnUrl;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ./Tests/
18 |
19 |
20 |
21 |
22 |
23 | ./
24 |
25 | ./Tests
26 | ./vendor
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Info/Period/Decorator/Memo.php:
--------------------------------------------------------------------------------
1 | info = $info;
22 |
23 | $this->setMemo($memo);
24 | }
25 |
26 | public function getInfo()
27 | {
28 | return $this->info->getInfo() +
29 | [
30 | 'PeriodMemo' => $this->memo,
31 | ];
32 | }
33 |
34 | protected function setMemo(string $memo)
35 | {
36 | if (mb_strlen($memo) > 255) {
37 | throw new \LogicException("unsupported length of this memo $memo");
38 | }
39 |
40 | $this->memo = $memo;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Info/Decorator/PayCancel.php:
--------------------------------------------------------------------------------
1 | info = $info;
23 |
24 | $this->setClientBackUrl($clientBackUrl);
25 | }
26 |
27 | public function getInfo()
28 | {
29 | return $this->info->getInfo() +
30 | [
31 | 'ClientBackURL' => $this->clientBackUrl,
32 | ];
33 | }
34 |
35 | protected function setClientBackUrl(string $clientBackUrl = null)
36 | {
37 | $this->clientBackUrl = $clientBackUrl;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Tests/MerchantTest.php:
--------------------------------------------------------------------------------
1 | decryptTradeInfo($encryptedStr);
21 |
22 | //assert
23 | $this->assertEquals($expected, $result);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableCredit.php:
--------------------------------------------------------------------------------
1 | quickCredit = $quickCredit;
18 | }
19 |
20 | public function getInfo()
21 | {
22 | $result = $this->info->getInfo() +
23 | [
24 | 'CREDIT' => $this->isEnable? 1: 0,
25 | ];
26 |
27 | if ($this->quickCredit) {
28 | $result += [
29 | 'TokenTerm' => $this->quickCredit->getTokenTerm(),
30 | 'TokenTermDemand' => $this->quickCredit->getTokenTermDemand(),
31 | ];
32 | }
33 |
34 | return $result;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Info/Period/BasicInfo.php:
--------------------------------------------------------------------------------
1 | 'JSON',
11 | 'TimeStamp' => time(),
12 | 'Version' => $this->version,
13 | 'MerOrderNo' => $this->order->getMerOrderNo(),
14 | 'ProdDesc' => $this->order->getProdDesc(),
15 | 'PeriodAmt' => $this->order->getAmount(),
16 | 'PeriodType' => $this->order->getPeriodType(),
17 | 'PeriodPoint' => $this->order->getPeriodPoint(),
18 | 'PeriodStartType' => $this->periodStartType,
19 | 'PeriodTimes' => $this->order->getPeriodTimes(),
20 | 'PayerEmail' => $this->contact->getPayerEmail(),
21 | 'EmailModify' => $this->contact->getPayerEmailModify(),
22 | 'PaymentInfo' => $this->contact->getPaymentInfo(),
23 | 'OrderInfo' => $this->contact->getOrderInfo(),
24 | ];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Info/AliPayBasicInfo.php:
--------------------------------------------------------------------------------
1 | $this->merchantId,
14 | 'RespondType' => 'JSON',
15 | 'TimeStamp' => time(),
16 | 'Version' => NewebPay::VERSION,
17 | 'NotifyURL' => $this->notifyUrl,
18 | 'Amt' => $this->order->getAmt(),
19 | 'ItemDesc' => $this->order->getItemDesc(),
20 | 'MerchantOrderNo' => $this->order->getMerchantOrderNo(),
21 | 'Email' => $this->payer->getEmail(),
22 | 'LoginType' => $this->payer->getLoginType()? 1: 0,
23 | // AliPay 必填
24 | 'Receiver' => $this->payer->getReceiver(),
25 | 'Tel1' => $this->payer->getTel1(),
26 | 'Tel2' => $this->payer->getTel2(),
27 | 'Count' => $this->count,
28 | ];
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Info/Decorator/Comment.php:
--------------------------------------------------------------------------------
1 | info = $info;
23 |
24 | $this->setComment($comment);
25 | }
26 |
27 | public function getInfo()
28 | {
29 | return $this->info->getInfo() +
30 | [
31 | 'OrderComment' => $this->comment,
32 | ];
33 | }
34 |
35 | protected function setComment(string $comment)
36 | {
37 | if (mb_strlen($comment) > 300) {
38 | throw new \LogicException('comment must less or equal than 300');
39 | }
40 |
41 | $this->comment = $comment;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2020 Lin Jinghong
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/Info/Decorator/TradeLimit.php:
--------------------------------------------------------------------------------
1 | info = $info;
25 |
26 | $this->setLimit($limit);
27 | }
28 |
29 | public function getInfo()
30 | {
31 | return $this->info->getInfo() +
32 | [
33 | 'TradeLimit' => $this->limit,
34 | ];
35 | }
36 |
37 | protected function setLimit(int $limit)
38 | {
39 | if (1 <= $limit && $limit <= 60) {
40 | $limit = 60;
41 | }
42 |
43 | if ($limit > 900) {
44 | $limit = 900;
45 | }
46 |
47 | $this->limit = $limit;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Info/Decorator/EnableCvsCom.php:
--------------------------------------------------------------------------------
1 | info = $info;
26 |
27 | $this->setType($type);
28 | }
29 |
30 | /**
31 | * 物流啟用
32 | *
33 | * @return array
34 | */
35 | public function getInfo()
36 | {
37 | return $this->info->getInfo() +
38 | [
39 | 'CVSCOM' => $this->type,
40 | ];
41 | }
42 |
43 | /**
44 | * @param int $type
45 | */
46 | protected function setType(int $type)
47 | {
48 | if ($type < 0 || $type > 3) {
49 | throw new \LogicException('Newebpay does not support this type');
50 | }
51 |
52 | $this->type = $type;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Info/Decorator/AliPayProduct.php:
--------------------------------------------------------------------------------
1 | info = $info;
25 |
26 | $this->index = $index;
27 |
28 | $this->product = $product;
29 | }
30 |
31 | public function getInfo()
32 | {
33 | return $this->info->getInfo() +
34 | [
35 | "Pid{$this->index}" => $this->product->getProductId(),
36 | "Title{$this->index}" => $this->product->getTitle(),
37 | "Desc{$this->index}" => $this->product->getDescription(),
38 | "Price{$this->index}" => $this->product->getPrice(),
39 | "Qty{$this->index}" => $this->product->getQuantity(),
40 | ];
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Contracts/Period/OrderInterface.php:
--------------------------------------------------------------------------------
1 | order = $order;
46 |
47 | $this->payer = $payer;
48 |
49 | $this->merchantId = $merchantId;
50 |
51 | $this->notifyUrl = $notifyUrl;
52 | }
53 |
54 | /**
55 | * @return string
56 | */
57 | public function getMerchantId()
58 | {
59 | return $this->merchantId;
60 | }
61 |
62 | /**
63 | * @return OrderInterface
64 | */
65 | public function getOrder()
66 | {
67 | return $this->order;
68 | }
69 |
70 | /**
71 | * @return PayerInterface
72 | */
73 | public function getPayer()
74 | {
75 | return $this->payer;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Info/Decorator/OfflinePay.php:
--------------------------------------------------------------------------------
1 | info = $info;
32 |
33 | $this->customerUrl = $customerUrl;
34 |
35 | $this->setTtl($ttl);
36 | }
37 |
38 | public function getInfo()
39 | {
40 | return $this->info->getInfo() +
41 | [
42 | 'ExpireDate' => $this->countExpireDate(),
43 | 'CustomerURL' => $this->customerUrl,
44 | ];
45 | }
46 |
47 | protected function setTtl(int $ttl = null)
48 | {
49 | if ($ttl <= 0) {
50 | throw new \LogicException('ttl must be large than 1');
51 | }
52 |
53 | if ($ttl > 180) {
54 | throw new \LogicException('ttl must be less than or equal to 180');
55 | }
56 |
57 | $this->ttl = $ttl;
58 | }
59 |
60 | /**
61 | * @return string
62 | */
63 | protected function countExpireDate()
64 | {
65 | if (! $this->ttl) {
66 | return '';
67 | }
68 |
69 | return date('Ymd', strtotime("+$this->ttl days"));
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Info/Period/Info.php:
--------------------------------------------------------------------------------
1 | order = $order;
53 |
54 | $this->contact = $contact;
55 |
56 | $this->setPeriodStartType($periodStartType);
57 |
58 | $this->setVersion($version);
59 | }
60 |
61 | protected function setPeriodStartType(string $periodStartType)
62 | {
63 | // if (! PeriodStartType::isValid($periodStartType)) {
64 | // throw new \LogicException("unsupported version $periodStartType");
65 | // }
66 |
67 | $this->periodStartType = $periodStartType;
68 | }
69 |
70 | protected function setVersion(string $version)
71 | {
72 | // if (! VersionType::isValid($version)) {
73 | // throw new \LogicException("unsupported version $version");
74 | // }
75 |
76 | $this->version = $version;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Merchant.php:
--------------------------------------------------------------------------------
1 | id = $id;
38 |
39 | $this->hashKey = $hashKey;
40 |
41 | $this->hashIv = $hashIv;
42 | }
43 |
44 | /**
45 | * 因為商城代號, hashKey, hashIv 是一整組的, 要就一次修改
46 | * response 是屬於個別商城的, 改商城順手清空response
47 | * @param string $id
48 | * @param string $hashKey
49 | * @param string $hashIv
50 | * @return $this
51 | */
52 | public function reset(string $id, string $hashKey, string $hashIv)
53 | {
54 | $this->id = $id;
55 | $this->hashKey = $hashKey;
56 | $this->hashIv = $hashIv;
57 |
58 | $this->response = null;
59 |
60 | return $this;
61 | }
62 |
63 | /**
64 | * 注入來自藍新通知的交易資訊
65 | * @param array $rawData
66 | * @return $this
67 | * @throws TradeInfoException
68 | */
69 | public function setRawData(array $rawData)
70 | {
71 | if (! isset($rawData['TradeInfo']) || ! isset($rawData['TradeSha'])) {
72 | throw new TradeInfoException('invalid data');
73 | }
74 |
75 | $this->response = new Response(
76 | json_decode($this->decryptTradeInfo($rawData['TradeInfo']), true),
77 | $rawData['TradeInfo'],
78 | $rawData['TradeSha']
79 | );
80 |
81 | return $this;
82 | }
83 |
84 | /**
85 | * 用來確認藍新通知的資料是否真的屬於此商城
86 | * @return bool
87 | */
88 | public function validateResponse()
89 | {
90 | if (! $this->response) {
91 | throw new \LogicException('set rawData first');
92 | }
93 |
94 | return $this->response->getTradeSha() === $this->countTradeSha($this->response->getTradeInfo());
95 | }
96 |
97 | /**
98 | * @return string
99 | */
100 | public function getId()
101 | {
102 | return $this->id;
103 | }
104 |
105 | /**
106 | * @return string
107 | */
108 | public function getHashKey(): string
109 | {
110 | return $this->hashKey;
111 | }
112 |
113 | /**
114 | * @return string
115 | */
116 | public function getHashIv()
117 | {
118 | return $this->hashIv;
119 | }
120 |
121 | /**
122 | * @return Response
123 | */
124 | public function getResponse()
125 | {
126 | return $this->response;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/Cryption.php:
--------------------------------------------------------------------------------
1 | getInfo();
22 |
23 | return $this->createEncryptedStr($infoPayload);
24 | }
25 |
26 | /**
27 | * @param string $tradeInfo
28 | * @return string
29 | */
30 | public function countTradeSha(string $tradeInfo)
31 | {
32 | if (! $tradeInfo) {
33 | throw new \LogicException('empty trade info');
34 | }
35 |
36 | return strtoupper(
37 | hash(
38 | "sha256",
39 | "HashKey={$this->hashKey}&{$tradeInfo}&HashIV={$this->hashIv}"
40 | )
41 | );
42 | }
43 |
44 | /**
45 | * @return string
46 | */
47 | public function getHashKey()
48 | {
49 | return $this->hashKey;
50 | }
51 |
52 | /**
53 | * @return string
54 | */
55 | public function getHashIv()
56 | {
57 | return $this->hashIv;
58 | }
59 |
60 | public function createEncryptedStr(array $infoPayload = [])
61 | {
62 | return trim(
63 | bin2hex(
64 | openssl_encrypt(
65 | $this->addPadding(http_build_query($infoPayload)),
66 | Cipher::METHOD,
67 | $this->hashKey,
68 | OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING,
69 | $this->hashIv
70 | )
71 | )
72 | );
73 | }
74 |
75 | /**
76 | * 從藍新回傳的加密交易資訊解成可讀的字串
77 | * @param string $tradeInfo
78 | * @return string
79 | * @throws TradeInfoException
80 | */
81 | public function decryptTradeInfo(string $tradeInfo)
82 | {
83 | if (! $tradeInfo) {
84 | throw new \LogicException('empty trade info');
85 | }
86 |
87 | return $this->stripPadding(
88 | openssl_decrypt(
89 | hex2bin($tradeInfo),
90 | Cipher::METHOD,
91 | $this->hashKey,
92 | OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING,
93 | $this->hashIv
94 | )
95 | );
96 | }
97 |
98 | protected function addPadding(string $string, int $blockSize = 32)
99 | {
100 | $len = strlen($string);
101 | $pad = $blockSize - ($len % $blockSize);
102 | $string .= str_repeat(chr($pad), $pad);
103 | return $string;
104 | }
105 |
106 | /**
107 | * @param $string
108 | * @return string
109 | * @throws TradeInfoException
110 | */
111 | protected function stripPadding($string)
112 | {
113 | $slast = ord(substr($string, -1));
114 | $slastc = chr($slast);
115 | $pcheck = substr($string, -$slast);
116 | if (preg_match("/$slastc{" . $slast . "}/", $string)) {
117 | $string = substr($string, 0, strlen($string) - $slast);
118 | return $string;
119 | }
120 |
121 | throw new TradeInfoException();
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Response.php:
--------------------------------------------------------------------------------
1 | data = $data;
25 |
26 | $this->tradeInfo = $tradeInfo;
27 |
28 | $this->tradeSha = $tradeSha;
29 | }
30 |
31 | /**
32 | * 回傳狀態
33 | * 成功: SUCCESS
34 | * 失敗: 錯誤代碼
35 | * @return string
36 | */
37 | public function getStatus()
38 | {
39 | return $this->data['Status'];
40 | }
41 |
42 | /**
43 | * 回傳訊息
44 | * @return string
45 | */
46 | public function getMessage()
47 | {
48 | return $this->data['Message'];
49 | }
50 |
51 | /**
52 | * 回傳參數細節
53 | * @return array
54 | */
55 | public function getResult()
56 | {
57 | return $this->data['Result'];
58 | }
59 |
60 | /**
61 | * 商城代號
62 | * @return string|null
63 | */
64 | public function getMerchantId()
65 | {
66 | return $this->data['Result']['MerchantID'] ?? null;
67 | }
68 |
69 | /**
70 | * 交易金額
71 | * @return int|null
72 | */
73 | public function getAmt()
74 | {
75 | return ((int) $this->data['Result']['Amt']) ?? null;
76 | }
77 |
78 | /**
79 | * 交易序號
80 | * @return string|null
81 | */
82 | public function getTradeNo()
83 | {
84 | return $this->data['Result']['TradeNo'] ?? null;
85 | }
86 |
87 | /**
88 | * 用來跟藍新溝通的訂單編號, 也就是OrderInterface 提供的MerchantOrderNo
89 | * @return string|null
90 | */
91 | public function getMerchantOrderNo()
92 | {
93 | return $this->data['Result']['MerchantOrderNo'] ?? null;
94 | }
95 |
96 | /**
97 | * 付款方式
98 | * @return string|null
99 | */
100 | public function getPaymentType()
101 | {
102 | return $this->data['Result']['PaymentType'] ?? null;
103 | }
104 |
105 | public function getResponseType()
106 | {
107 | return $this->data['Result']['ResponseType'] ?? null;
108 | }
109 |
110 | /**
111 | * 付款完成時間
112 | * @return string|null
113 | */
114 | public function getPayTime()
115 | {
116 | return $this->data['Result']['PayTime'] ?? null;
117 | }
118 |
119 | /**
120 | * 付款人取號或交易時的ip
121 | * @return string|null
122 | */
123 | public function getIp()
124 | {
125 | return $this->data['Result']['IP'] ?? null;
126 | }
127 |
128 | /**
129 | * 款項保管銀行
130 | * @return string|null
131 | */
132 | public function getEscrowBank()
133 | {
134 | return $this->data['Result']['EscrowBank'] ?? null;
135 | }
136 |
137 | /**
138 | * 離線金流會帶繳費到期日回來, 格式: yyyy-mm-dd
139 | * @return string|null
140 | */
141 | public function getExpireDate()
142 | {
143 | return $this->data['Result']['ExpireDate'] ?? null;
144 | }
145 |
146 | /**
147 | * @return string
148 | */
149 | public function getTradeInfo()
150 | {
151 | return $this->tradeInfo;
152 | }
153 |
154 | /**
155 | * @return string
156 | */
157 | public function getTradeSha()
158 | {
159 | return $this->tradeSha;
160 | }
161 |
162 | /**
163 | * @return array
164 | */
165 | public function getData()
166 | {
167 | return $this->data;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/Tests/InfoTest.php:
--------------------------------------------------------------------------------
1 | getMockBuilder(OrderInterface::class)
32 | ->getMock();
33 |
34 | $order->expects($this->once())
35 | ->method('getMerchantOrderNo')
36 | ->willReturn($orderMerchantNo = (string) time());
37 |
38 | $order->expects($this->once())
39 | ->method('getItemDesc')
40 | ->willReturn($itemDesc = 'This is an apple');
41 |
42 | $order->expects($this->once())
43 | ->method('getAmt')
44 | ->willReturn($amt = 100);
45 |
46 | $payer = $this->getMockBuilder(PayerInterface::class)
47 | ->getMock();
48 |
49 | $payer->expects($this->once())
50 | ->method('getEmail')
51 | ->willReturn($email = 'foobar@gg.cc');
52 |
53 | $payer->expects($this->once())
54 | ->method('getLoginType')
55 | ->willReturn($loginType = false);
56 |
57 | $ttl = 3;
58 |
59 | $customerUrl = 'customer.url';
60 |
61 | //act
62 | $info =
63 | new PayComplete(
64 | new OfflinePay(
65 | new PayCancel(
66 | new Language(
67 | new PayerEmailEditable(
68 | new BasicInfo($merchant->getId(), $notifyUrl, $order, $payer),
69 | $email
70 | ),
71 | $lang = LanguageType::EN
72 | ),
73 | $clientBackUrl
74 | ),
75 | $customerUrl,
76 | $ttl
77 | ),
78 | $returnUrl
79 | )
80 | ;
81 | $result = $info->getInfo();
82 |
83 | //assert
84 | $this->assertArrayHasKey('MerchantID', $result);
85 | $this->assertEquals($merchant->getId(), $result['MerchantID']);
86 |
87 | $this->assertArrayHasKey('RespondType', $result);
88 | $this->assertEquals('JSON', $result['RespondType']);
89 |
90 | $this->assertArrayHasKey('TimeStamp', $result);
91 |
92 | $this->assertArrayHasKey('Version', $result);
93 | $this->assertEquals(NewebPay::VERSION, $result['Version']);
94 |
95 | $this->assertArrayHasKey('NotifyURL', $result);
96 | $this->assertEquals($notifyUrl, $result['NotifyURL']);
97 |
98 | $this->assertArrayHasKey('Amt', $result);
99 | $this->assertEquals($amt, $result['Amt']);
100 |
101 | $this->assertArrayHasKey('ItemDesc', $result);
102 | $this->assertEquals($itemDesc, $result['ItemDesc']);
103 |
104 | $this->assertArrayHasKey('MerchantOrderNo', $result);
105 | $this->assertEquals($orderMerchantNo, $result['MerchantOrderNo']);
106 |
107 | $this->assertArrayHasKey('Email', $result);
108 | $this->assertEquals($email, $result['Email']);
109 |
110 | $this->assertArrayHasKey('LoginType', $result);
111 | $this->assertEquals($loginType, $result['LoginType']);
112 |
113 | $this->assertArrayHasKey('EmailModify', $result);
114 | $this->assertEquals(1, $result['EmailModify']);
115 |
116 | $this->assertArrayHasKey('LangType', $result);
117 | $this->assertEquals($lang, $result['LangType']);
118 |
119 | $this->assertArrayHasKey('ClientBackURL', $result);
120 | $this->assertEquals($clientBackUrl, $result['ClientBackURL']);
121 |
122 | $this->assertArrayHasKey('ExpireDate', $result);
123 |
124 | $this->assertArrayHasKey('CustomerURL', $result);
125 | $this->assertEquals($customerUrl, $result['CustomerURL']);
126 |
127 | $this->assertArrayHasKey('ReturnURL', $result);
128 | $this->assertEquals($returnUrl, $result['ReturnURL']);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/Tests/NewebPayTest.php:
--------------------------------------------------------------------------------
1 | getMockBuilder(Merchant::class)
34 | ->disableOriginalConstructor()
35 | ->getMock();
36 |
37 | $merchant->expects($this->atLeastOnce())
38 | ->method('getId')
39 | ->willReturn($merchantId);
40 |
41 | $merchant->expects($this->once())
42 | ->method('countTradeInfo')
43 | ->willReturn($tradeInfo = '12345');
44 |
45 | $merchant->expects($this->once())
46 | ->method('countTradeSha')
47 | ->willReturn($tradeSha = '22345');
48 |
49 | $email = 'test@gg.cc';
50 |
51 | $returnUrl= 'return.url';
52 |
53 | $notifyUrl = 'notify.url';
54 |
55 | $clientBackUrl = 'client.back.url';
56 |
57 | $order = $this->getMockBuilder(OrderInterface::class)
58 | ->getMock();
59 |
60 | $payer = $this->getMockBuilder(PayerInterface::class)
61 | ->getMock();
62 |
63 | $ttl = 3;
64 |
65 | $customerUrl = 'customer.url';
66 |
67 | $info = new BasicInfo($merchant->getId(), $notifyUrl, $order, $payer);
68 | $info = new PayerEmailEditable($info, $email);
69 | $info = new Language($info, LanguageType::EN);
70 | $info = new PayCancel($info, $clientBackUrl);
71 | $info = new OfflinePay($info, $customerUrl, $ttl);
72 | $info = new PayComplete($info, $returnUrl);
73 |
74 | $expected = <<
76 |
77 |
78 |
79 |
80 |
81 | EOT;
82 |
83 |
84 | //act
85 | $result = $newebpay
86 | ->setIsProduction(false)
87 | ->setMerchant($merchant)
88 | ->generateForm($info);
89 |
90 | //assert
91 | $this->assertEquals($expected, $result);
92 | }
93 |
94 | public function test_checkoutByApi()
95 | {
96 | //arrange
97 | $newebpay = new NewebPay();
98 |
99 | $merchantId = 'merchant.id.123';
100 |
101 | $hashKey = 'hash.key.234';
102 |
103 | $hashIv = 'hash.iv.34567890';
104 |
105 | // $merchant = new Merchant($merchantId, $hashKey, $hashIv);
106 |
107 | $merchant = $this->getMockBuilder(Merchant::class)
108 | ->disableOriginalConstructor()
109 | ->getMock();
110 |
111 | $merchant->expects($this->atLeastOnce())
112 | ->method('getId')
113 | ->willReturn($merchantId);
114 |
115 | $merchant->expects($this->once())
116 | ->method('countTradeInfo')
117 | ->willReturn($tradeInfo = '12345');
118 |
119 | $merchant->expects($this->once())
120 | ->method('countTradeSha')
121 | ->willReturn($tradeSha = '22345');
122 |
123 | $email = 'test@gg.cc';
124 |
125 | $returnUrl= 'return.url';
126 |
127 | $notifyUrl = 'notify.url';
128 |
129 | $clientBackUrl = 'client.back.url';
130 |
131 | $order = $this->getMockBuilder(OrderInterface::class)
132 | ->getMock();
133 |
134 | $payer = $this->getMockBuilder(PayerInterface::class)
135 | ->getMock();
136 |
137 | $ttl = 3;
138 |
139 | $customerUrl = 'customer.url';
140 |
141 | $info = new BasicInfo($merchant->getId(), $notifyUrl, $order, $payer);
142 | $info = new PayerEmailEditable($info, $email);
143 | $info = new Language($info, LanguageType::EN);
144 | $info = new PayCancel($info, $clientBackUrl);
145 | $info = new OfflinePay($info, $customerUrl, $ttl);
146 | $info = new PayComplete($info, $returnUrl);
147 |
148 | $expected = [
149 | 'url' => 'https://ccore.newebpay.com/MPG/mpg_gateway',
150 | 'form_params' => [
151 | 'MerchantID' => $merchantId,
152 | 'TradeInfo' => $tradeInfo,
153 | 'TradeSha' => $tradeSha,
154 | 'Version' => '1.5',
155 | ],
156 | ];
157 |
158 | //act
159 | $result = $newebpay
160 | ->setIsProduction(false)
161 | ->setMerchant($merchant)
162 | ->checkoutForApi($info);
163 |
164 | //assert
165 | $this->assertEquals($expected, $result);
166 |
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/Tests/AliPayInfoTest.php:
--------------------------------------------------------------------------------
1 | getMockBuilder(OrderInterface::class)
22 | ->getMock();
23 |
24 | $order->expects($this->once())
25 | ->method('getMerchantOrderNo')
26 | ->willReturn($orderMerchantNo = (string) time());
27 |
28 | $order->expects($this->once())
29 | ->method('getItemDesc')
30 | ->willReturn($itemDesc = 'This is an apple');
31 |
32 | $order->expects($this->once())
33 | ->method('getAmt')
34 | ->willReturn($amt = 100);
35 |
36 | $payer = $this->getMockBuilder(AliPayPayerInterface::class)
37 | ->getMock();
38 |
39 | $payer->expects($this->once())
40 | ->method('getEmail')
41 | ->willReturn($email = 'foobar@gg.cc');
42 |
43 | $payer->expects($this->once())
44 | ->method('getLoginType')
45 | ->willReturn($loginType = false);
46 |
47 | $payer->expects($this->once())
48 | ->method('getReceiver')
49 | ->willReturn('foobar');
50 |
51 | $payer->expects($this->once())
52 | ->method('getTel1')
53 | ->willReturn('0988999000');
54 |
55 | $payer->expects($this->once())
56 | ->method('getTel2')
57 | ->willReturn('0988777666');
58 |
59 | $merchantId = 'merchant.id.223';
60 |
61 | $merchant = new Merchant($merchantId, $hashKey = 'hash.key.123', $hashIv = 'hash.iv.4567890');
62 |
63 | $notifyUrl = 'notify.url';
64 |
65 | $count = 3;
66 |
67 | $product1 = $this->getMockBuilder(AliPayProductInterface::class)
68 | ->getMock();
69 |
70 | $product1->expects($this->once())
71 | ->method('getProductId')
72 | ->willReturn($p1id = time());
73 |
74 | $product1->expects($this->once())
75 | ->method('getTitle')
76 | ->willReturn($p1title = 'title1');
77 |
78 | $product1->expects($this->once())
79 | ->method('getDescription')
80 | ->willReturn($p1desc = 'this is a big apple
');
81 |
82 | $product1->expects($this->once())
83 | ->method('getPrice')
84 | ->willReturn($p1price = 100);
85 |
86 | $product1->expects($this->once())
87 | ->method('getQuantity')
88 | ->willReturn($p1qty = 2);
89 |
90 | $product2 = $this->getMockBuilder(AliPayProductInterface::class)
91 | ->getMock();
92 |
93 | $product2->expects($this->once())
94 | ->method('getProductId')
95 | ->willReturn($p2id = time());
96 |
97 | $product2->expects($this->once())
98 | ->method('getTitle')
99 | ->willReturn($p2title = 'title2');
100 |
101 | $product2->expects($this->once())
102 | ->method('getDescription')
103 | ->willReturn($p2desc = 'this is a big camera
');
104 |
105 | $product2->expects($this->once())
106 | ->method('getPrice')
107 | ->willReturn($p2price = 1000);
108 |
109 | $product2->expects($this->once())
110 | ->method('getQuantity')
111 | ->willReturn($p2qty = 1);
112 |
113 | //act
114 | $info = new AliPayBasicInfo($merchant->getId(), $notifyUrl, $order, $payer, $count);
115 | $info = new AliPayProduct($info, 1, $product1);
116 | $info = new AliPayProduct($info, 2, $product2);
117 | $info = new EnableAliPay($info);
118 | $info = new EnableCredit($info);
119 | $result = $info->getInfo();
120 |
121 | //assert
122 | $this->assertArrayHasKey('MerchantID', $result);
123 | $this->assertEquals($merchantId, $result['MerchantID']);
124 |
125 | $this->assertArrayHasKey('RespondType', $result);
126 | $this->assertEquals('JSON', $result['RespondType']);
127 |
128 | $this->assertArrayHasKey('TimeStamp', $result);
129 |
130 | $this->assertArrayHasKey('Version', $result);
131 | $this->assertEquals(NewebPay::VERSION, $result['Version']);
132 |
133 | $this->assertArrayHasKey('NotifyURL', $result);
134 | $this->assertEquals($notifyUrl, $result['NotifyURL']);
135 |
136 | $this->assertArrayHasKey('Amt', $result);
137 | $this->assertEquals($amt, $result['Amt']);
138 |
139 | $this->assertArrayHasKey('ItemDesc', $result);
140 | $this->assertEquals($itemDesc, $result['ItemDesc']);
141 |
142 | $this->assertArrayHasKey('MerchantOrderNo', $result);
143 | $this->assertEquals($orderMerchantNo, $result['MerchantOrderNo']);
144 |
145 | $this->assertArrayHasKey('Email', $result);
146 | $this->assertEquals($email, $result['Email']);
147 |
148 | $this->assertArrayHasKey('LoginType', $result);
149 | $this->assertEquals($loginType, $result['LoginType']);
150 |
151 | $this->assertArrayHasKey('Pid1', $result);
152 | $this->assertEquals($p1id, $result['Pid1']);
153 |
154 | $this->assertArrayHasKey('Title1', $result);
155 | $this->assertEquals($p1title, $result['Title1']);
156 |
157 | $this->assertArrayHasKey('Desc1', $result);
158 | $this->assertEquals($p1desc, $result['Desc1']);
159 |
160 | $this->assertArrayHasKey('Price1', $result);
161 | $this->assertEquals($p1price, $result['Price1']);
162 |
163 | $this->assertArrayHasKey('Qty1', $result);
164 | $this->assertEquals($p1qty, $result['Qty1']);
165 |
166 | $this->assertArrayHasKey('Pid2', $result);
167 | $this->assertEquals($p2id, $result['Pid2']);
168 |
169 | $this->assertArrayHasKey('Title2', $result);
170 | $this->assertEquals($p2title, $result['Title2']);
171 |
172 | $this->assertArrayHasKey('Desc2', $result);
173 | $this->assertEquals($p2desc, $result['Desc2']);
174 |
175 | $this->assertArrayHasKey('Price2', $result);
176 | $this->assertEquals($p2price, $result['Price2']);
177 |
178 | $this->assertArrayHasKey('Qty2', $result);
179 | $this->assertEquals($p2qty, $result['Qty2']);
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Newebpay 藍新金流
2 |
3 | 程式版本 1.5
4 | 文件版本 1.0.4
5 | [Official Doc](https://www.newebpay.com/website/Page/content/download_api#1)
6 |
7 | ## feature
8 |
9 | - [x] 多功能收款 MPG
10 | - [x] 單筆交易狀態查詢
11 | - [ ] 信用卡取消授權
12 | - [ ] 信用卡請退款
13 | - [x] 信用卡定期定額
14 |
15 | ## How to use
16 |
17 | ### 信用卡定期定額
18 |
19 | #### 建立交易資訊 (BasicInfo)
20 | - $order: 你的訂單物件, 務必實作package 中的OrderInterface
21 | - $contact: 你的付款人物件, 務必實作package 中的ContactInterface
22 | - $periodStartType: 檢查卡號模式, 參閱文件p.13
23 | - $version: 串接程式版本
24 | - 帶入 1.0 版本,則[背面末三碼]將為必填欄位
25 | - 帶入 1.1 版本,則[背面末三碼]將為非必填
26 | ```php
27 | $info = new \fall1600\Package\Newebpay\Info\Period\BasicInfo($order, $payer, $periodStartType, $version);
28 | ```
29 |
30 | #### 選填參數
31 |
32 | ```php
33 |
34 | $info = new ReturnUrl($info, 'https://your.return.url');
35 | $info = new NotifyUrl($info, 'https://your.notify.url');
36 | $info = new BackUrl($info, 'https://your.back.url');
37 | $info = new Memo($info, 'your memo here');
38 | ```
39 |
40 | #### 建立NewebPay 物件, 注入商店資訊, 帶著交易資訊前往藍新申請定期定額
41 | - $merchantId: 你在藍新商店代號
42 | - $hashKey: 你在藍新商店專屬的HashKey
43 | - $hashIv: 你在藍新商店專屬的HashIV
44 |
45 | ```php
46 | $newebpay = new NewebPay();
47 | $newebpay
48 | ->setIsProduction(false) // 設定環境, 預設就是走正式機
49 | ->setMerchant(new Merchant($merchantId, $hashKey, $hashIv))
50 | ->issue($info);
51 | ```
52 |
53 | #### 請在你的訂單物件實作 OrderInterface
54 |
55 | ```php
56 |
92 |
93 | ### 多功能收款
94 |
95 | #### 建立交易資訊 (BasicInfo)
96 | - $merchantId: 你在藍新申請的商店代號
97 | - $notifyUrl: 用來接收藍新付款通知的callback url
98 | - $order: 你的訂單物件, 務必實作package 中的OrderInterface
99 | - $payer: 你的付款人物件, 務必實作package 中的PayerInterface
100 |
101 | ```php
102 | $info = new BasicInfo($merchantId, $notifyUrl, $order, $payer);
103 | ```
104 |
105 | #### 依場景開啟各種付款方式, 有需要再啟用即可
106 | ```php
107 | // 啟用信用卡
108 | $info = new EnableCredit($info);
109 | // 啟用3, 6, 12 期分期付款
110 | $info = new PayInInstallments($info, '3,6,12');
111 | // 啟用超商條碼
112 | $info = new EnableBarcode($info);
113 | // 啟用Google Pay
114 | $info = new EnableGooglePay($info);
115 | // 啟用Web ATM
116 | $info = new EnableWebAtm($info);
117 | // 搭配非即時交易, 設定藍新通知付款資訊的callback url, 以及繳費期限
118 | $info = new OfflinePay($info, $customerUrl, $ttl);
119 | // 在藍新交易完成後導回的網址
120 | $info = new PayComplete($info, $returnUrl);
121 | // 設定讓消費者在付款頁按下返回時可以回導的頁面
122 | $info = new PayCancel($info, $clientBackUrl);
123 |
124 | // 文件末有付款方式對應表
125 | ```
126 |
127 | #### 建立NewebPay 物件, 注入商店資訊, 帶著交易資訊前往藍新付款
128 | - $merchantId: 你在藍新商店代號
129 | - $hashKey: 你在藍新商店專屬的HashKey
130 | - $hashIv: 你在藍新商店專屬的HashIV
131 |
132 | ```php
133 | $newebpay = new NewebPay();
134 | $newebpay
135 | ->setIsProduction(false) // 設定環境, 預設就是走正式機
136 | ->setMerchant(new Merchant($merchantId, $hashKey, $hashIv))
137 | ->checkout($info);
138 | ```
139 |
140 | #### 請在你的訂單物件實作 OrderInterface
141 |
142 | ```php
143 | setRawData($request->all())->validateResponse(); //確認為true 後再往下走
174 |
175 | // response 封裝了通知交易的結果, 以下僅列常用methods
176 | $response = $merchant->getResponse();
177 | // 付款成敗
178 | $response->getStatus();
179 | // 取得交易序號
180 | $response->getTradeNo();
181 | // 取得訂單編號, 就是OrderInterface 實作的getMerchantOrderNo
182 | $response->getMerchantOrderNo();
183 | // 付款時間
184 | $response->getPayTime();
185 | ```
186 |
187 | #### 支付寶 (AliPay)
188 |
189 | 支付寶付款需提供的參數更多
190 |
191 | - $payer: 你的付款人物件, 務必實作package 中的AliPayPayerInterface
192 | - $numberOfProducts: 此訂單的品項數量(integer)
193 | - $product1: 第一個品項(實作AliPayProductInterface)
194 | - $product2: 第二個品項(實作AliPayProductInterface)
195 |
196 | ```php
197 | $info = new AliPayBasicInfo($merchantId, $notifyUrl, $order, $payer, $numberOfProducts);
198 | $info = new EnableAliPay($info);
199 |
200 | $info = new AliPayProduct($info, 1, $product1);
201 | $info = new AliPayProduct($info, 2, $product2);
202 | ```
203 |
204 | #### 單筆交易查詢
205 | ```php
206 | $resp = $newebpay
207 | ->setMerchant($merchant)
208 | ->query($order);
209 | ```
210 |
211 |
212 | #### 各種url 你分的清楚嗎?
213 | | Name | 用途 | 設定的物件 | 備註 |
214 | |:-----------------|:------------------------------------ |:-------------|:---------------------------------------------------------|
215 | | NotifyURL | 通知你系統交易資訊的callback url | BasicInfo | 通常用在訂單付款狀態切換, 最重要,所以BasicInfo 就要設定了 |
216 | | CustomerURL | 離線付款取號完成通知你系統的callback url | OfflinePay | 用在紀錄離線付款的取號, 務必設定 |
217 | | ReturnURL | 付款完成後要回到你系統的位置 | PayComplete | 沒設定就是顯示在藍新 |
218 | | ClientBackURL | 交易取消時回到你系統的位置 | PayCancel | 沒設定就是顯示在藍新 |
219 |
220 |
221 | #### 付款方式對應的物件
222 |
223 | | Class | 付款方式 | 交易性質 |
224 | |:------------------|:------------------------|:------------------|
225 | | EnableCredit | 信用卡(一次結清) | 即時交易 |
226 | | EnableWebAtm | WebATM | 即時交易 |
227 | | EnableVacc | ATM 轉帳 | 非即時交易 |
228 | | EnableCvs | 超商代碼繳費 | 非即時交易 |
229 | | EnableBarcode | 超商條碼繳費 | 非即時交易 |
230 | | EnableCvsCom | 超商取貨付款 | 非即時交易 |
231 | | EnableAliPay | 支付寶 | 非即時交易 |
232 | | EnableEzPay | ezPay 電子錢包 | 即時交易, 非即時交易 |
233 | | EnableLinePay | LINE Pay | 即時交易 |
234 |
235 |
--------------------------------------------------------------------------------
/NewebPay.php:
--------------------------------------------------------------------------------
1 | merchant) {
111 | throw new \LogicException('empty merchant');
112 | }
113 |
114 | $url = $this->isProduction? static::ALTER_AMT_URL_PRODUCTION: static::ALTER_AMT_URL_TEST;
115 |
116 | $data = [
117 | 'RespondType' => 'JSON',
118 | 'Version' => '1.0',
119 | 'MerOrderNo' => $orderNo,
120 | 'PeriodNo' => $periodNo,
121 | 'TimeStamp' => time(),
122 | 'PeriodType' => $periodType,
123 | 'PeriodPoint' => $periodPoint,
124 | ];
125 |
126 | if ($alterAmt) {
127 | $data['AlterAmt'] = $alterAmt;
128 | }
129 |
130 | $payload = [
131 | 'MerchantID_' => $this->merchant->getId(),
132 | 'PostData_' => $this->merchant->createEncryptedStr($data),
133 | ];
134 |
135 | $resp = $this->post($url, $payload);
136 | if (! isset($resp['period'])) {
137 | throw new TradeInfoException("interface change from Newebpay");
138 | }
139 |
140 | return json_decode($this->merchant->decryptTradeInfo($resp['period']), true);
141 | }
142 |
143 | /**
144 | * @param string $orderNo
145 | * @param string $periodNo
146 | * @param string $alterType
147 | * @return array
148 | * @throws TradeInfoException
149 | */
150 | public function alterStatus(string $orderNo, string $periodNo, string $alterType)
151 | {
152 | if (! $this->merchant) {
153 | throw new \LogicException('empty merchant');
154 | }
155 |
156 | $url = $this->isProduction? static::ALTER_STATUS_URL_PRODUCTION: static::ALTER_STATUS_URL_TEST;
157 |
158 | $payload = [
159 | 'MerchantID_' => $this->merchant->getId(),
160 | 'PostData_' => $this->merchant->createEncryptedStr(
161 | [
162 | 'RespondType' => 'JSON',
163 | 'Version' => '1.0',
164 | 'MerOrderNo' => $orderNo,
165 | 'PeriodNo' => $periodNo,
166 | 'AlterType' => $alterType,
167 | 'TimeStamp' => time(),
168 | ]
169 | ),
170 | ];
171 |
172 | $resp = $this->post($url, $payload);
173 | if (! isset($resp['period'])) {
174 | throw new TradeInfoException("interface change from Newebpay");
175 | }
176 |
177 | return json_decode($this->merchant->decryptTradeInfo($resp['period']), true);
178 | }
179 |
180 | public function issue(PeriodInfo $info)
181 | {
182 | echo <<
184 |
185 |
186 |
187 |
188 |
189 | {$this->generatePeriodForm($info)}
190 |
194 |
195 |
196 | EOT;
197 | }
198 |
199 | public function generatePeriodForm(PeriodInfo $info)
200 | {
201 | if (! $this->merchant) {
202 | throw new \LogicException('empty merchant');
203 | }
204 |
205 | $url = $this->isProduction? static::ISSUE_URL_PRODUCTION: static::ISSUE_URL_TEST;
206 |
207 | $tradeInfo = $this->merchant->countTradeInfo($info);
208 |
209 | return <<
211 |
212 |
213 |
214 | EOT;
215 | }
216 |
217 | public function checkout(Info $info)
218 | {
219 | echo <<
221 |
222 |
223 |
224 |
225 |
226 | {$this->generateForm($info)}
227 |
231 |
232 |
233 | EOT;
234 | }
235 |
236 | /**
237 | * @param Info $info
238 | * @return string
239 | */
240 | public function generateForm(Info $info)
241 | {
242 | if (! $this->merchant) {
243 | throw new \LogicException('empty merchant');
244 | }
245 |
246 | $url = $this->isProduction ? static::CHECKOUT_URL_PRODUCTION: static::CHECKOUT_URL_TEST;
247 |
248 | $tradeInfo = $this->merchant->countTradeInfo($info);
249 |
250 | $tradeSha = $this->merchant->countTradeSha($tradeInfo);
251 |
252 | $version = static::VERSION;
253 |
254 | return <<
256 |
257 |
258 |
259 |
260 |
261 | EOT;
262 | }
263 |
264 | public function checkoutForApi(Info $info)
265 | {
266 | if (! $this->merchant) {
267 | throw new \LogicException('empty merchant');
268 | }
269 |
270 | return [
271 | 'url' => $this->isProduction ? static::CHECKOUT_URL_PRODUCTION: static::CHECKOUT_URL_TEST,
272 | 'form_params' => [
273 | 'MerchantID' => $this->merchant->getId(),
274 | 'TradeInfo' => $tradeInfo = $this->merchant->countTradeInfo($info),
275 | 'TradeSha' => $this->merchant->countTradeSha($tradeInfo),
276 | 'Version' => static::VERSION,
277 | ],
278 | ];
279 | }
280 |
281 | /**
282 | * @param OrderInterface $order
283 | * @return array
284 | */
285 | public function query(OrderInterface $order)
286 | {
287 | if (! $this->merchant) {
288 | throw new \LogicException('empty merchant');
289 | }
290 |
291 | $url = $this->isProduction? static::QUERY_URL_PRODUCTION: static::QUERY_URL_TEST;
292 |
293 | $payload = [
294 | 'MerchantID' => $this->merchant->getId(),
295 | 'Version' => '1.1',
296 | 'RespondType' => 'JSON',
297 | 'CheckValue' => $this->countCheckValue($order),
298 | 'TimeStamp' => time(),
299 | 'MerchantOrderNo' => $order->getMerchantOrderNo(),
300 | 'Amt' => $order->getAmt(),
301 | ];
302 |
303 | return $this->post($url, $payload);
304 | }
305 |
306 | public function setIsProduction(bool $isProduction)
307 | {
308 | $this->isProduction = $isProduction;
309 |
310 | return $this;
311 | }
312 |
313 | public function setFormId(string $formId)
314 | {
315 | $this->formId = $formId;
316 |
317 | return $this;
318 | }
319 |
320 | public function setMerchant(Merchant $merchant)
321 | {
322 | $this->merchant = $merchant;
323 |
324 | return $this;
325 | }
326 |
327 | /**
328 | * @param OrderInterface $order
329 | * @return string
330 | */
331 | protected function countCheckValue(OrderInterface $order)
332 | {
333 | $payload = [
334 | 'IV' => $this->merchant->getHashIv(),
335 | 'Amt' => $order->getAmt(),
336 | 'MerchantID' => $this->merchant->getId(),
337 | 'MerchantOrderNo' => $order->getMerchantOrderNo(),
338 | 'Key' => $this->merchant->getHashKey(),
339 | ];
340 |
341 | return strtoupper(hash('sha256', http_build_query($payload)));
342 | }
343 |
344 | protected function post(string $url, array $payload)
345 | {
346 | $ch = curl_init($url);
347 | curl_setopt($ch, CURLOPT_POST, 1);
348 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
349 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($payload));
350 | $result = curl_exec($ch);
351 | curl_close($ch);
352 |
353 | return json_decode($result, true);
354 | }
355 | }
356 |
--------------------------------------------------------------------------------