Class: VietnamQrPay::QRPay
- Inherits:
-
Object
- Object
- VietnamQrPay::QRPay
- Defined in:
- lib/vietnam_qr_pay/qr_pay.rb
Overview
Core object for parsing, inspecting, and building Vietnamese payment QR payloads.
The object keeps a mutable in-memory representation of the EMVCo TLV payload so callers can decode an existing string, change selected fields, and rebuild a valid payload with a regenerated CRC.
Instance Attribute Summary collapse
-
#additional_data ⇒ Object
Returns the value of attribute additional_data.
-
#amount ⇒ Object
Returns the value of attribute amount.
-
#category ⇒ Object
Returns the value of attribute category.
-
#city ⇒ Object
Returns the value of attribute city.
-
#consumer ⇒ Object
Returns the value of attribute consumer.
-
#crc ⇒ Object
Returns the value of attribute crc.
-
#currency ⇒ Object
Returns the value of attribute currency.
-
#evmco ⇒ Object
Returns the value of attribute evmco.
-
#init_method ⇒ Object
Returns the value of attribute init_method.
-
#merchant ⇒ Object
Returns the value of attribute merchant.
-
#nation ⇒ Object
Returns the value of attribute nation.
-
#provider ⇒ Object
Returns the value of attribute provider.
-
#tip_and_fee_amount ⇒ Object
Returns the value of attribute tip_and_fee_amount.
-
#tip_and_fee_percent ⇒ Object
Returns the value of attribute tip_and_fee_percent.
-
#tip_and_fee_type ⇒ Object
Returns the value of attribute tip_and_fee_type.
-
#unreserved ⇒ Object
Returns the value of attribute unreserved.
-
#version ⇒ Object
Returns the value of attribute version.
-
#zip_code ⇒ Object
Returns the value of attribute zip_code.
Class Method Summary collapse
-
.field_data(id, value) ⇒ Object
Serializes a single TLV field.
-
.gen_crc_code(content) ⇒ Object
Returns the CRC value formatted as the upper-case 4-character suffix expected by EMVCo payloads.
-
.init_viet_qr(bank_bin:, bank_number:, amount: nil, purpose: nil, service: VietQRService::BY_ACCOUNT_NUMBER) ⇒ Object
Builds a VietQR-oriented object with sensible defaults for static or dynamic transfers.
-
.init_vnpay_qr(merchant_id:, merchant_name:, store:, terminal:, amount: nil, purpose: nil, bill_number: nil, mobile_number: nil, loyalty_number: nil, reference: nil, customer_label: nil) ⇒ Object
Builds a VNPayQR-oriented object with the merchant block prepopulated.
-
.verify_crc(content) ⇒ Object
Verifies the trailing CRC against the payload prefix.
Instance Method Summary collapse
-
#build ⇒ Object
Builds a new payload from the current in-memory structure.
-
#initialize(content = nil) ⇒ QRPay
constructor
A new instance of QRPay.
-
#parse(content) ⇒ Object
Parses an existing payload.
-
#set_evmco_field(id, value) ⇒ Object
Assigns an EMVCo extension field in the 65-79 range.
-
#set_unreserved_field(id, value) ⇒ Object
Assigns an unreserved extension field in the 80-99 range.
- #valid? ⇒ Boolean (also: #is_valid)
Constructor Details
#initialize(content = nil) ⇒ QRPay
Returns a new instance of QRPay.
29 30 31 32 33 34 35 36 37 38 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 29 def initialize(content = nil) @valid = true @provider = Provider.new @consumer = Consumer.new @merchant = Merchant.new @additional_data = AdditionalData.new @evmco = {} @unreserved = {} parse(content) if content && !content.empty? end |
Instance Attribute Details
#additional_data ⇒ Object
Returns the value of attribute additional_data.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def additional_data @additional_data end |
#amount ⇒ Object
Returns the value of attribute amount.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def amount @amount end |
#category ⇒ Object
Returns the value of attribute category.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def category @category end |
#city ⇒ Object
Returns the value of attribute city.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def city @city end |
#consumer ⇒ Object
Returns the value of attribute consumer.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def consumer @consumer end |
#crc ⇒ Object
Returns the value of attribute crc.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def crc @crc end |
#currency ⇒ Object
Returns the value of attribute currency.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def currency @currency end |
#evmco ⇒ Object
Returns the value of attribute evmco.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def evmco @evmco end |
#init_method ⇒ Object
Returns the value of attribute init_method.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def init_method @init_method end |
#merchant ⇒ Object
Returns the value of attribute merchant.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def merchant @merchant end |
#nation ⇒ Object
Returns the value of attribute nation.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def nation @nation end |
#provider ⇒ Object
Returns the value of attribute provider.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def provider @provider end |
#tip_and_fee_amount ⇒ Object
Returns the value of attribute tip_and_fee_amount.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def tip_and_fee_amount @tip_and_fee_amount end |
#tip_and_fee_percent ⇒ Object
Returns the value of attribute tip_and_fee_percent.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def tip_and_fee_percent @tip_and_fee_percent end |
#tip_and_fee_type ⇒ Object
Returns the value of attribute tip_and_fee_type.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def tip_and_fee_type @tip_and_fee_type end |
#unreserved ⇒ Object
Returns the value of attribute unreserved.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def unreserved @unreserved end |
#version ⇒ Object
Returns the value of attribute version.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def version @version end |
#zip_code ⇒ Object
Returns the value of attribute zip_code.
10 11 12 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 10 def zip_code @zip_code end |
Class Method Details
.field_data(id, value) ⇒ Object
Serializes a single TLV field. Invalid or blank values are omitted.
194 195 196 197 198 199 200 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 194 def field_data(id, value) field_id = id.to_s field_value = value.to_s return "" if field_id.length != 2 || field_value.empty? "#{field_id}#{format('%02d', field_value.length)}#{field_value}" end |
.gen_crc_code(content) ⇒ Object
Returns the CRC value formatted as the upper-case 4-character suffix expected by EMVCo payloads.
189 190 191 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 189 def gen_crc_code(content) format("%04X", CRC16.crc16ccitt(content)) end |
.init_viet_qr(bank_bin:, bank_number:, amount: nil, purpose: nil, service: VietQRService::BY_ACCOUNT_NUMBER) ⇒ Object
Builds a VietQR-oriented object with sensible defaults for static or dynamic transfers.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 134 def init_viet_qr(bank_bin:, bank_number:, amount: nil, purpose: nil, service: VietQRService::BY_ACCOUNT_NUMBER) qr = new # VietQR uses init method 11 for static QR and 12 when amount is fixed. qr.init_method = amount ? "12" : "11" qr.provider.field_id = FieldID::VIETQR qr.provider.guid = QRProviderGUID::VIETQR qr.provider.name = QRProvider::VIETQR qr.provider.service = service qr.consumer.bank_bin = bank_bin qr.consumer.bank_number = bank_number qr.amount = amount qr.additional_data.purpose = purpose qr end |
.init_vnpay_qr(merchant_id:, merchant_name:, store:, terminal:, amount: nil, purpose: nil, bill_number: nil, mobile_number: nil, loyalty_number: nil, reference: nil, customer_label: nil) ⇒ Object
Builds a VNPayQR-oriented object with the merchant block prepopulated.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 150 def init_vnpay_qr( merchant_id:, merchant_name:, store:, terminal:, amount: nil, purpose: nil, bill_number: nil, mobile_number: nil, loyalty_number: nil, reference: nil, customer_label: nil ) qr = new qr.merchant.id = merchant_id qr.merchant.name = merchant_name qr.provider.field_id = FieldID::VNPAYQR qr.provider.guid = QRProviderGUID::VNPAY qr.provider.name = QRProvider::VNPAY qr.amount = amount qr.additional_data.purpose = purpose qr.additional_data.bill_number = bill_number qr.additional_data.mobile_number = mobile_number qr.additional_data.store = store qr.additional_data.terminal = terminal qr.additional_data.loyalty_number = loyalty_number qr.additional_data.reference = reference qr.additional_data.customer_label = customer_label qr end |
.verify_crc(content) ⇒ Object
Verifies the trailing CRC against the payload prefix.
182 183 184 185 186 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 182 def verify_crc(content) check_content = content[0...-4] crc_code = content[-4, 4].to_s.upcase crc_code == gen_crc_code(check_content) end |
Instance Method Details
#build ⇒ Object
Builds a new payload from the current in-memory structure.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 61 def build provider_data_content = case provider.guid when QRProviderGUID::VIETQR # VietQR stores bank BIN and account/card number inside a nested # provider template rather than directly on the root payload. [ self.class.field_data(VietQRConsumerFieldID::BANK_BIN, consumer.bank_bin), self.class.field_data(VietQRConsumerFieldID::BANK_NUMBER, consumer.bank_number) ].join when QRProviderGUID::VNPAY # VNPayQR stores the merchant identifier directly in provider data. merchant.id.to_s else # Unknown providers are preserved so decode -> build stays lossless. provider.data.to_s end provider_content = [ self.class.field_data(ProviderFieldID::GUID, provider.guid), self.class.field_data(ProviderFieldID::DATA, provider_data_content), self.class.field_data(ProviderFieldID::SERVICE, provider.service) ].join additional_data_content = [ self.class.field_data(AdditionalDataID::BILL_NUMBER, additional_data.bill_number), self.class.field_data(AdditionalDataID::MOBILE_NUMBER, additional_data.mobile_number), self.class.field_data(AdditionalDataID::STORE_LABEL, additional_data.store), self.class.field_data(AdditionalDataID::LOYALTY_NUMBER, additional_data.loyalty_number), self.class.field_data(AdditionalDataID::REFERENCE_LABEL, additional_data.reference), self.class.field_data(AdditionalDataID::CUSTOMER_LABEL, additional_data.customer_label), self.class.field_data(AdditionalDataID::TERMINAL_LABEL, additional_data.terminal), self.class.field_data(AdditionalDataID::PURPOSE_OF_TRANSACTION, additional_data.purpose), self.class.field_data(AdditionalDataID::ADDITIONAL_CONSUMER_DATA_REQUEST, additional_data.data_request) ].join # The CRC field contains the field ID and fixed length before the value # is calculated over the complete payload prefix. content = [ self.class.field_data(FieldID::VERSION, version || "01"), self.class.field_data(FieldID::INIT_METHOD, init_method || "11"), self.class.field_data(provider.field_id, provider_content), self.class.field_data(FieldID::CATEGORY, category), self.class.field_data(FieldID::CURRENCY, currency || "704"), self.class.field_data(FieldID::AMOUNT, amount), self.class.field_data(FieldID::TIP_AND_FEE_TYPE, tip_and_fee_type), self.class.field_data(FieldID::TIP_AND_FEE_AMOUNT, tip_and_fee_amount), self.class.field_data(FieldID::TIP_AND_FEE_PERCENT, tip_and_fee_percent), self.class.field_data(FieldID::NATION, nation || "VN"), self.class.field_data(FieldID::MERCHANT_NAME, merchant.name), self.class.field_data(FieldID::CITY, city), self.class.field_data(FieldID::ZIP_CODE, zip_code), self.class.field_data(FieldID::ADDITIONAL_DATA, additional_data_content), build_fields(evmco), build_fields(unreserved), "#{FieldID::CRC}04" ].join content + self.class.gen_crc_code(content) end |
#parse(content) ⇒ Object
Parses an existing payload.
Parsing always resets the current object first so reusing a QRPay instance cannot leak fields from a previous payload.
50 51 52 53 54 55 56 57 58 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 50 def parse(content) reset_parsed_state return invalidate! if content.nil? || content.length < 4 return invalidate! unless self.class.verify_crc(content) parse_root_content(content) self end |
#set_evmco_field(id, value) ⇒ Object
Assigns an EMVCo extension field in the 65-79 range.
123 124 125 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 123 def set_evmco_field(id, value) evmco[id.to_s] = value.to_s end |
#set_unreserved_field(id, value) ⇒ Object
Assigns an unreserved extension field in the 80-99 range.
128 129 130 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 128 def set_unreserved_field(id, value) unreserved[id.to_s] = value.to_s end |
#valid? ⇒ Boolean Also known as: is_valid
40 41 42 |
# File 'lib/vietnam_qr_pay/qr_pay.rb', line 40 def valid? @valid end |