fix : qr code generation to be dynamic
This commit is contained in:
@@ -213,33 +213,38 @@ class Foodcourt::QrpayController < BaseFoodcourtController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
qr_str = "00020101021226480015com.mmqrpay.www0115223743000377032020600000052045814530310454075000.005802MM5908Code2LAB6006Yangon61051101062730125ORDER_1747826957_5ae9cb900523MMQR1075L175919500000010813PAY_BY_QRCODE64300002MY0109ကုဒ်တူလက်0207ရန်ကုန်6304AD40";
|
@paymethod = PaymentMethodSetting.find_by(payment_method: "MMQR")
|
||||||
@string_to_encode = qr_str
|
|
||||||
|
|
||||||
qrcode = RQRCode::QRCode.new(@string_to_encode)
|
|
||||||
|
|
||||||
@qr_svg = qrcode.as_svg(
|
@merchant = KbzMerchant.new(@paymethod)
|
||||||
color: "000", # Hex color for the QR code modules (black)
|
|
||||||
shape_rendering: "crispEdges", # Renders sharp edges
|
|
||||||
module_size: 2, # Size of each module (dot/square)
|
|
||||||
standalone: true, # Outputs a complete SVG document
|
|
||||||
use_path: true # Modern SVG path rendering
|
|
||||||
)
|
|
||||||
|
|
||||||
png_data = qrcode.as_png(
|
@response = @merchant.create_order(amount: @sale_data.grand_total, merch_order_id: @sale_data.receipt_no)
|
||||||
bit_depth: 1,
|
@qr_string = @response['qrCode']
|
||||||
border_modules: 4,
|
|
||||||
color_mode: ChunkyPNG::COLOR_GRAYSCALE,
|
|
||||||
color: 'black',
|
|
||||||
file: nil,
|
|
||||||
fill: 'white',
|
|
||||||
module_px_size: 8, # Pixel size of each module
|
|
||||||
resize_exactly_to: false,
|
|
||||||
resize_gte_to: false,
|
|
||||||
size: 240 # Approximate size of the image in pixels (e.g., 240x240)
|
|
||||||
).to_s
|
|
||||||
|
|
||||||
ActionCable.server.broadcast('second_display_view_channel', { data: qr_str, qr_svg: @qr_svg })
|
qrcode = RQRCode::QRCode.new(@qr_string)
|
||||||
|
|
||||||
|
@qr_svg = qrcode.as_svg(
|
||||||
|
color: "000",
|
||||||
|
shape_rendering: "crispEdges",
|
||||||
|
module_size: 2,
|
||||||
|
standalone: true,
|
||||||
|
use_path: true
|
||||||
|
)
|
||||||
|
|
||||||
|
# png_data = qrcode.as_png(
|
||||||
|
# bit_depth: 1,
|
||||||
|
# border_modules: 4,
|
||||||
|
# color_mode: ChunkyPNG::COLOR_GRAYSCALE,
|
||||||
|
# color: 'black',
|
||||||
|
# file: nil,
|
||||||
|
# fill: 'white',
|
||||||
|
# module_px_size: 8, # Pixel size of each module
|
||||||
|
# resize_exactly_to: false,
|
||||||
|
# resize_gte_to: false,
|
||||||
|
# size: 240 # Approximate size of the image in pixels (e.g., 240x240)
|
||||||
|
# ).to_s
|
||||||
|
|
||||||
|
ActionCable.server.broadcast('second_display_view_channel', { data: @qr_string, qr_svg: @qr_svg })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,20 +5,18 @@ class KbzMerchant
|
|||||||
|
|
||||||
def initialize(payment_method)
|
def initialize(payment_method)
|
||||||
@payment_method = payment_method
|
@payment_method = payment_method
|
||||||
@api_url = payment_method.test_mode? ?
|
@api_url ='http://api.kbzpay.com/payment/gateway/uat/precreate'
|
||||||
'http://api.kbzpay.com/payment/gateway/uat/precreate' :
|
|
||||||
'https://api.kbzpay.com/payment/gateway/precreate'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_order(transaction_params)
|
def create_order(amount:, merch_order_id:)
|
||||||
payload = build_payload(transaction_params)
|
payload = build_payload(amount, merch_order_id)
|
||||||
response = send_request(payload)
|
response = send_request(payload)
|
||||||
handle_response(response)
|
handle_response(response)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def build_payload(params)
|
def build_payload(amount, merch_order_id, timeout='120m')
|
||||||
base_params = {
|
base_params = {
|
||||||
method: 'kbz.payment.precreate',
|
method: 'kbz.payment.precreate',
|
||||||
timestamp: Time.now.utc.to_i.to_s,
|
timestamp: Time.now.utc.to_i.to_s,
|
||||||
@@ -27,13 +25,13 @@ class KbzMerchant
|
|||||||
sign_type: 'SHA256',
|
sign_type: 'SHA256',
|
||||||
version: '1.0',
|
version: '1.0',
|
||||||
biz_content: {
|
biz_content: {
|
||||||
appid: @payment_method.appid,
|
appid: @payment_method.gateway_url,
|
||||||
merch_code: @payment_method.merch_code,
|
merch_code: @payment_method.merchant_account_id,
|
||||||
merch_order_id: params[:merch_order_id],
|
merch_order_id: merch_order_id,
|
||||||
trade_type: 'PAY_BY_QRCODE',
|
trade_type: 'PAY_BY_QRCODE',
|
||||||
total_amount: params[:total_amount].to_s,
|
total_amount: amount.to_s,
|
||||||
trans_currency: 'MMK',
|
trans_currency: 'MMK',
|
||||||
timeout_express: params[:timeout] || '120m'
|
timeout_express: timeout || '120m'
|
||||||
}.compact
|
}.compact
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,11 +51,10 @@ class KbzMerchant
|
|||||||
end
|
end
|
||||||
|
|
||||||
def generate_signature(flattened_params)
|
def generate_signature(flattened_params)
|
||||||
key = 'uatfoodcourt@12'
|
|
||||||
sorted_params = flattened_params.except('sign', 'sign_type').sort
|
sorted_params = flattened_params.except('sign', 'sign_type').sort
|
||||||
string_a = sorted_params.map { |k, v| "#{k}=#{v}" }.join('&')
|
string_a = sorted_params.map { |k, v| "#{k}=#{v}" }.join('&')
|
||||||
puts "String a: #{string_a}"
|
puts "String a: #{string_a}"
|
||||||
string_to_sign = "#{string_a}&key=#{key}"
|
string_to_sign = "#{string_a}&key=#{@payment_method.auth_token}"
|
||||||
puts "String to sign: #{string_to_sign}"
|
puts "String to sign: #{string_to_sign}"
|
||||||
Digest::SHA256.hexdigest(string_to_sign).upcase
|
Digest::SHA256.hexdigest(string_to_sign).upcase
|
||||||
end
|
end
|
||||||
@@ -78,8 +75,8 @@ class KbzMerchant
|
|||||||
body: { Request: payload }.to_json,
|
body: { Request: payload }.to_json,
|
||||||
timeout: 15
|
timeout: 15
|
||||||
)
|
)
|
||||||
|
response.body
|
||||||
puts "Response: #{response}"
|
# puts "Response: #{response}"
|
||||||
rescue HTTParty::Error => e
|
rescue HTTParty::Error => e
|
||||||
raise PaymentError, "HTTP error: #{e.message}"
|
raise PaymentError, "HTTP error: #{e.message}"
|
||||||
rescue SocketError => e
|
rescue SocketError => e
|
||||||
@@ -89,7 +86,7 @@ class KbzMerchant
|
|||||||
|
|
||||||
|
|
||||||
def handle_response(response)
|
def handle_response(response)
|
||||||
json_response = JSON.parse(response.body)
|
json_response = JSON.parse(response)
|
||||||
if json_response.dig('Response', 'result') == 'SUCCESS'
|
if json_response.dig('Response', 'result') == 'SUCCESS'
|
||||||
json_response['Response']
|
json_response['Response']
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,57 +1,125 @@
|
|||||||
<div class="row clearfix h-100">
|
<div class="row clearfix h-100">
|
||||||
<div class="col-lg-6 col-md-6 col-sm-6 h-100">
|
<div class="col-lg-6 col-md-6 col-sm-6 h-100">
|
||||||
<div class="card h-100" style="opacity: 0.75;">
|
<div class="card h-100" style="opacity: 0.85; background-color: #f8f9fa;">
|
||||||
|
<div class="card-header bg-primary text-white">
|
||||||
|
<h4 class="mb-0">Invoice Details</h4>
|
||||||
|
</div>
|
||||||
<div class="card-block h-100">
|
<div class="card-block h-100">
|
||||||
<div class="card-text">
|
<div class="card-text">
|
||||||
<div id="order-detail-slimscroll" data-height="190">
|
<div id="order-detail-slimscroll" style="max-height: 60vh; overflow-y: auto;">
|
||||||
<table class="table table-striped second_display_items" id="order-items-table">
|
<table class="table table-striped second_display_items" id="order-items-table">
|
||||||
<thead>
|
<thead class="thead-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th class="item-name">Items</th>
|
<th class="item-name">Items</th>
|
||||||
<th class="item-">QTY</th>
|
<th class="item-qty">QTY</th>
|
||||||
<th class="item-">Price</th>
|
<th class="item-price">Price</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<!-- Items will be populated here -->
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer bg-light">
|
||||||
<table class="table" border="0">
|
<table class="table table-borderless">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="charges-name"><strong>Sub Total:</strong></td>
|
<td class="charges-name"><strong>Sub Total:</strong></td>
|
||||||
<td></td>
|
<td class="text-right item-attr"><strong id="s_sub_total">0.00</strong></td>
|
||||||
<td></td>
|
|
||||||
<td class="item-attr"><strong id="s_sub_total">0.00</strong></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="charges-name"><strong>Discount Amount:</strong></td>
|
<td class="charges-name"><strong>Discount:</strong></td>
|
||||||
<td></td>
|
<td class="text-right item-attr"><strong id="s_total_discount">0.00</strong></td>
|
||||||
<td></td>
|
|
||||||
<td class="item-attr"><strong id="s_total_discount">0.00</strong></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="charges-name"><strong>Tax Amount:</strong></td>
|
<td class="charges-name"><strong>Tax Amount:</strong></td>
|
||||||
<td></td>
|
<td class="text-right item-attr"><strong id="s_tatal_tax">0.00</strong></td>
|
||||||
<td></td>
|
|
||||||
<td class="item-attr"><strong id="s_tatal_tax">0.00</strong></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr class="table-active">
|
||||||
<td class="charges-name"><strong>Grand:</strong></td>
|
<td class="charges-name"><strong>Grand Total:</strong></td>
|
||||||
<td></td>
|
<td class="text-right item-attr"><strong id="s_grand_total">0.00</strong></td>
|
||||||
<td></td>
|
|
||||||
<td class="item-attr"><strong id="s_grand_total">0.00</strong></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-6 col-md-6 col-sm-6 h-100">
|
|
||||||
<div id="qrpay_svg" style="background-color: #ddd;">
|
<div class="col-lg-6 col-md-6 col-sm-6 h-100 d-flex flex-column">
|
||||||
|
<div class="card h-100" style="opacity: 0.85;">
|
||||||
|
<div class="card-header text-white" style="background-color: #ffc107;">
|
||||||
|
<h4 class="mb-0">Payment Options</h4>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="card-body d-flex flex-column align-items-center justify-content-center">
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<h5>Scan to Pay</h5>
|
||||||
|
<p class="text-muted">Use your mobile wallet app</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- QR Code Container -->
|
||||||
|
<div id="qr-payment-container" class="text-center p-3 bg-white rounded border" style="max-width: 300px;">
|
||||||
|
<div id="qr-code" class="mb-2">
|
||||||
|
<!-- QR Code will be generated here -->
|
||||||
|
<div class="">
|
||||||
|
<div id="qrpay_svg">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="payment-details">
|
||||||
|
<p class="mb-1"><strong>Amount:</strong> <span id="qr-amount"><%= number_to_currency(@total_amount) %></span></p>
|
||||||
|
<p class="mb-1"><strong>Invoice #:</strong> <span id="qr-invoice"><%= @invoice_id %></span></p>
|
||||||
|
<p class="text-muted small">Expires in 15 minutes</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="payment-instructions mt-4 text-center">
|
||||||
|
<h6>How to Pay:</h6>
|
||||||
|
<ol class="text-left small pl-3">
|
||||||
|
<li>Open your mobile wallet app</li>
|
||||||
|
<li>Tap on 'Scan QR Code'</li>
|
||||||
|
<li>Point your camera at this code</li>
|
||||||
|
<li>Confirm the payment details</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Add these styles to your existing CSS */
|
||||||
|
.card {
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
border-radius: 8px 8px 0 0 !important;
|
||||||
|
padding: 12px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead th {
|
||||||
|
border-bottom: 2px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#qr-payment-container {
|
||||||
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-instructions {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-active {
|
||||||
|
background-color: rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -677,6 +677,8 @@ scope "(:locale)", locale: /en|mm/ do
|
|||||||
get 'sale/:sale_id' => 'sales#show'
|
get 'sale/:sale_id' => 'sales#show'
|
||||||
get 'order/:order_id' => "orders#show"
|
get 'order/:order_id' => "orders#show"
|
||||||
|
|
||||||
|
get 'qrpay/payment_loading' => 'qrpay#payment_loading'
|
||||||
|
|
||||||
# Other Charges
|
# Other Charges
|
||||||
get "/:sale_id/:type/other_charges" => "other_charges#index"
|
get "/:sale_id/:type/other_charges" => "other_charges#index"
|
||||||
post "/:sale_id/other_charges" => "other_charges#create"
|
post "/:sale_id/other_charges" => "other_charges#create"
|
||||||
|
|||||||
Reference in New Issue
Block a user