Completed
Push — master ( 196d3f...3efd24 )
by Monthon
03:27
created

PromptPay::CRC16HexDigest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace vansales;
4
5
require_once './vendor/autoload.php';
6
7
use Endroid\QrCode\QrCode;
8
9
/**
10
 * Original code logic from https://github.com/kittinan/php-promptpay-qr
11
 * More information https://www.vansalesapp.com
12
 */
13
14
class PromptPay
15
{
16
17
  const ID_PAYLOAD_FORMAT = '00';
18
  const ID_POI_METHOD = '01';
19
  const ID_MERCHANT_INFORMATION_BOT = '29';
20
  const ID_TRANSACTION_CURRENCY = '53';
21
  const ID_TRANSACTION_AMOUNT = '54';
22
  const ID_COUNTRY_CODE = '58';
23
  const ID_CRC = '63';
24
25
  const PAYLOAD_FORMAT_EMV_QRCPS_MERCHANT_PRESENTED_MODE = '01';
26
  const POI_METHOD_STATIC = '11';
27
  const POI_METHOD_DYNAMIC = '12';
28
  const MERCHANT_INFORMATION_TEMPLATE_ID_GUID = '00';
29
  const BOT_ID_MERCHANT_PHONE_NUMBER = '01';
30
  const BOT_ID_MERCHANT_TAX_ID = '02';
31
  const BOT_ID_MERCHANT_EWALLET_ID = '03';
32
  const GUID_PROMPTPAY = 'A000000677010111';
33
  const TRANSACTION_CURRENCY_THB = '764';
34
  const COUNTRY_CODE_TH = 'TH';
35
36
  public function generatePayload($target, $amount = null)
37
  {
38
39
    $target = $this->sanitizeTarget($target);
40
41
    $targetType = strlen($target) >= 15 ? self::BOT_ID_MERCHANT_EWALLET_ID : (strlen($target) >= 13 ? self::BOT_ID_MERCHANT_TAX_ID : self::BOT_ID_MERCHANT_PHONE_NUMBER);
42
43
    $data = [
44
      $this->f(self::ID_PAYLOAD_FORMAT, self::PAYLOAD_FORMAT_EMV_QRCPS_MERCHANT_PRESENTED_MODE),
45
      $this->f(self::ID_POI_METHOD, $amount ? self::POI_METHOD_DYNAMIC : self::POI_METHOD_STATIC),
46
      $this->f(self::ID_MERCHANT_INFORMATION_BOT, $this->serialize([
47
        $this->f(self::MERCHANT_INFORMATION_TEMPLATE_ID_GUID, self::GUID_PROMPTPAY),
48
        $this->f($targetType, $this->formatTarget($target))
49
      ])),
50
      $this->f(self::ID_COUNTRY_CODE, self::COUNTRY_CODE_TH),
51
      $this->f(self::ID_TRANSACTION_CURRENCY, self::TRANSACTION_CURRENCY_THB),
52
    ];
53
54
    if ($amount !== null) {
55
      array_push($data, $this->f(self::ID_TRANSACTION_AMOUNT, $this->formatAmount($amount)));
56
    }
57
58
    $dataToCrc = $this->serialize($data) . self::ID_CRC . '04';
59
    array_push($data, $this->f(self::ID_CRC, $this->CRC16HexDigest($dataToCrc)));
60
    return $this->serialize($data);
61
  }
62
63
  public function f($id, $value)
64
  {
65
    return implode('', [$id, substr('00' . strlen($value), -2), $value]);
66
  }
67
68
  public function serialize($xs)
69
  {
70
    return implode('', $xs);
71
  }
72
73 1
  public function sanitizeTarget($str)
74
  {
75 1
    $str = preg_replace('/[^0-9]/', '', $str);
76 1
    return $str;
77
  }
78
79 1
  public function formatTarget($target)
80
  {
81
82 1
    $str = $this->sanitizeTarget($target);
83 1
    if (strlen($str) >= 13) {
84 1
      return $str;
85
    }
86
87 1
    $str = preg_replace('/^0/', '66', $str);
88 1
    $str = '0000000000000' . $str;
89
90 1
    return substr($str, -13);
91
  }
92
93 1
  public function formatAmount($amount)
94
  {
95 1
    return number_format($amount, 2, '.', '');
96
  }
97
98 1
  public function crc16($data)
99
  {
100 1
    $crc = 0xFFFF;
101 1
    for ($i = 0; $i < strlen($data); $i++) {
102 1
      $x = (($crc >> 8) ^ ord($data[$i])) & 0xFF;
103 1
      $x ^= $x >> 4;
104 1
      $crc = (($crc << 8) ^ ($x << 12) ^ ($x << 5) ^ $x) & 0xFFFF;
105
    }
106 1
    return $crc;
107
  }
108
109
  /*
110
  * Returns CRC16 of a string as hexadecimal string 
111
  */
112 1
  public function CRC16HexDigest($str)
113
  {
114 1
    return sprintf('%04X', $this->crc16($str));
115
  }
116
117
118
  public function generateQrCode($target, $amount = 0)
119
  {
120
121
    $payload = $this->generatePayload($target, $amount);
122
123
    $qrCode = new QrCode($payload);
124
    header('Content-Type: ' . $qrCode->getContentType());
125
    echo $qrCode->writeString();
126
  }
127
}
128