#!/usr/bin/env php
<?php
// Generate PromptPay Payload With and without Amount
// Inspired and code logic from https://github.com/dtinth/promptpay-qr
// More information https://www.blognone.com/node/95133
// Usage:  pp.php PHONENUMBER [AMOUNT]

class PromptPay {
	const ID_PAYLOAD_FORMAT = '00';
	const ID_POI_METHOD = '01';
	const ID_MERCHANT_INFORMATION_BOT = '29';
	const ID_TRANSACTION_CURRENCY = '53';
	const ID_TRANSACTION_AMOUNT = '54';
	const ID_COUNTRY_CODE = '58';
	const ID_CRC = '63';
	const PAYLOAD_FORMAT_EMV_QRCPS_MERCHANT_PRESENTED_MODE = '01';
	const POI_METHOD_STATIC = '11';
	const POI_METHOD_DYNAMIC = '12';
	const MERCHANT_INFORMATION_TEMPLATE_ID_GUID = '00';
	const BOT_ID_MERCHANT_PHONE_NUMBER = '01';
	const BOT_ID_MERCHANT_TAX_ID = '02';
	const BOT_ID_MERCHANT_EWALLET_ID = '03';
	const GUID_PROMPTPAY = 'A000000677010111';
	const TRANSACTION_CURRENCY_THB = '764';
	const COUNTRY_CODE_TH = 'TH';
	const CRC_LEN = '04';

	public function generatePayload($target, $amount = null) {
		$target = $this->sanitizeTarget($target);
		if(strlen($target) != 15 && strlen($target) != 13 && strlen($target) != 10) {
			echo "ERROR: Target must be 10, 13 or 15 long: ".$target."\n";
			exit(1);
		}

		$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);
		$amounData = "";
		if ($amount !== null) {
			$amountData = $this->field(self::ID_TRANSACTION_AMOUNT, $this->formatAmount($amount));
		}
		$data = $this->serialize([
			$this->field(self::ID_PAYLOAD_FORMAT, self::PAYLOAD_FORMAT_EMV_QRCPS_MERCHANT_PRESENTED_MODE),
			$this->field(self::ID_POI_METHOD, $amount ? self::POI_METHOD_DYNAMIC : self::POI_METHOD_STATIC),
			$this->field(self::ID_MERCHANT_INFORMATION_BOT, $this->serialize([
				$this->field(self::MERCHANT_INFORMATION_TEMPLATE_ID_GUID, self::GUID_PROMPTPAY),
				$this->field($targetType, $this->formatTarget($target))
			])),
			$this->field(self::ID_TRANSACTION_CURRENCY, self::TRANSACTION_CURRENCY_THB),
			$amountData,
			$this->field(self::ID_COUNTRY_CODE, self::COUNTRY_CODE_TH),
		]);
		$data .= self::ID_CRC.self::CRC_LEN;
		return $data.$this->crc16($data);
	}

	public function field($id, $value) {
		return $id.substr('0' . strlen($value), -2).$value;
	}

	public function serialize($arr) {
		return implode('', $arr);
	}

	public function sanitizeTarget($str) {
		return preg_replace('/[^0-9]/', '', $str);
	}

	public function formatTarget($str) {
		if (strlen($str) == 10) {
			$str = preg_replace('/^0/', '0066', $str);
			if (strlen($str) != 13) {
				echo "ERROR: Phone number must start with zero: ".$str."\n";
				exit(2);
			}
		}
		return $str;
	}

	public function formatAmount($amount) {
		return number_format($amount, 2, '.', '');
	}

	public function crc16($data) {
		return trim(shell_exec('./crc16.py "'.$data.'"'));
	}
}

// Process commandline
if(count($argv) < 2 || count($argv) > 3){
	echo "Usage:  pp.php PHONENUMBER [AMOUNT]\n";
	exit(0);
}

// Generate PrompPay QR
$pp = new PromptPay();
$num = $pp->SanitizeTarget($argv[1]);
if(count($argv) == 2){
	$content = $pp->generatePayload($num);
}else{
	$amount = $pp->formatAmount($argv[2]);
	$content = $pp->generatePayload($num, $amount);
	$num .= "_".$amount;
}
exec('qr -p promptpay.png -o PromptPay'.$num.'.png "'.$content.'"');
exit(0);

// Generate PromptPay Payload With and without Amount
// target stripped of spaces and dashes (all non-numerics)
// target[9]: Phone number
// target[13]: Tax ID or CitizenID
// target[15]: eWallet
$target = '089-999-9999';
$amount = 420;
echo $pp->generatePayload($target, $amount);
echo "00020101021229370016A000000677010111011300668999999995802TH53037645406420.006304CF9E\n";
echo $pp->generatePayload($target);
echo "00020101021129370016A000000677010111011300668999999995802TH53037646304FE29\n";
$target = '1234567890123';
echo $pp->generatePayload($target);
echo "00020101021129370016A000000677010111021312345678901235802TH53037646304EC40\n";
$target = '123456789012345';
echo $pp->generatePayload($target);
echo "00020101021129390016A00000067701011103151234567890123455802TH5303764630473AF\n";
exit(0);
