PromptPay::serialize()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace KS;
4
5
/**
6
 * Inspired and code logic from https://github.com/dtinth/promptpay-qr
7
 * More information https://www.blognone.com/node/95133
8
 */
9
class PromptPay {
10
11
  const ID_PAYLOAD_FORMAT = '00';
12
  const ID_POI_METHOD = '01';
13
  const ID_MERCHANT_INFORMATION_BOT = '29';
14
  const ID_TRANSACTION_CURRENCY = '53';
15
  const ID_TRANSACTION_AMOUNT = '54';
16
  const ID_COUNTRY_CODE = '58';
17
  const ID_CRC = '63';
18
  
19
  const PAYLOAD_FORMAT_EMV_QRCPS_MERCHANT_PRESENTED_MODE = '01';
20
  const POI_METHOD_STATIC = '11';
21
  const POI_METHOD_DYNAMIC = '12';
22
  const MERCHANT_INFORMATION_TEMPLATE_ID_GUID = '00';
23
  const BOT_ID_MERCHANT_PHONE_NUMBER = '01';
24
  const BOT_ID_MERCHANT_TAX_ID = '02';
25
  const BOT_ID_MERCHANT_EWALLET_ID = '03';
26
  const GUID_PROMPTPAY = 'A000000677010111';
27
  const TRANSACTION_CURRENCY_THB = '764';
28
  const COUNTRY_CODE_TH = 'TH';
29
30 2
  public function generatePayload($target, $amount = null) {
31
32 2
    $target = $this->sanitizeTarget($target);
33
34 2
    $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);
35
36
    $data = [
37 2
      $this->f(self::ID_PAYLOAD_FORMAT, self::PAYLOAD_FORMAT_EMV_QRCPS_MERCHANT_PRESENTED_MODE),
38 2
      $this->f(self::ID_POI_METHOD, $amount ? self::POI_METHOD_DYNAMIC : self::POI_METHOD_STATIC),
39 2
      $this->f(self::ID_MERCHANT_INFORMATION_BOT, $this->serialize([
40 2
          $this->f(self::MERCHANT_INFORMATION_TEMPLATE_ID_GUID, self::GUID_PROMPTPAY),
41 2
          $this->f($targetType, $this->formatTarget($target))
42
      ])),
43 2
      $this->f(self::ID_COUNTRY_CODE, self::COUNTRY_CODE_TH),
44 2
      $this->f(self::ID_TRANSACTION_CURRENCY, self::TRANSACTION_CURRENCY_THB),
45
    ];
46
    
47 2
    if ($amount !== null) {
48 2
      array_push($data, $this->f(self::ID_TRANSACTION_AMOUNT, $this->formatAmount($amount)));
49
    }
50
    
51 2
    $dataToCrc = $this->serialize($data) . self::ID_CRC . '04';
52 2
    array_push($data, $this->f(self::ID_CRC, $this->crc16($dataToCrc)));
53 2
    return $this->serialize($data);
54
  }
55
56 3
  public function f($id, $value) {
57 3
    return implode('', [$id, substr('00' . strlen($value), -2), $value]);
58
  }
59
  
60 2
  public function serialize($xs) {
61 2
    return implode('', $xs);
62
  }
63
  
64 3
  public function sanitizeTarget($str) {
65 3
    $str = preg_replace('/[^0-9]/', '', $str);
66 3
    return $str;
67
  }
68
69 3
  public function formatTarget($target) {
70
    
71 3
    $str = $this->sanitizeTarget($target);
72 3
    if (strlen($str) >= 13) {
73 2
      return $str;
74
    }
75
    
76 3
    $str = preg_replace('/^0/', '66', $str);
77 3
    $str = '0000000000000' . $str;
78
79 3
    return substr($str, -13);
80
  }
81
82 3
  public function formatAmount($amount) {
83 3
    return number_format($amount, 2, '.', '');
84
  }
85
86 3
  public function crc16($data) {
87 3
    $crc16 = new \mermshaus\CRC\CRC16CCITT();
88 3
    $crc16->update($data);
89 3
    $checksum = $crc16->finish();
90 3
    return strtoupper(bin2hex($checksum));
91
  }
92
  
93 1
  public function generateQrCode($savePath, $target, $amount = null, $width = 500) {
94
95 1
    $payload = $this->generatePayload($target, $amount);
96
97 1
    $renderer = new \BaconQrCode\Renderer\Image\Png();
98 1
    $renderer->setHeight($width);
99 1
    $renderer->setWidth($width);
100 1
    $renderer->setMargin(0);
101 1
    $writer = new \BaconQrCode\Writer($renderer);
102 1
    $writer->writeFile($payload, $savePath);
103 1
  }
104
105
}
106