Files
sx-fc/app/services/kbz_merchant.rb
Dev Team 555de796c4 Revert "fixes"
This reverts commit 174a232421.
2025-06-02 17:21:41 +06:30

200 lines
5.5 KiB
Ruby

require 'httparty'
class KbzMerchant
class PaymentError < StandardError; end
def initialize(payment_method)
@payment_method = payment_method
@url = @payment_method.gateway_url
json_params = @payment_method.additional_parameters.inspect.undump
params = JSON.parse(json_params)
@notify_url = params['notify_url']
@app_id = params['app_id']
end
def create_order(amount:, merch_order_id:, timeout: '120m')
api_url = "#{@url}/precreate"
payload = build_create_payload(amount, merch_order_id, timeout)
response = send_request(payload, api_url)
handle_response(response)
end
def close_order(merch_order_id:)
api_url = "#{@url}/closeorder"
payload = build_close_payload(merch_order_id)
response = send_request(payload, api_url)
handle_response(response)
end
def query_order(merch_order_id:)
api_url = "#{@url}/queryorder"
payload = build_query_payload(merch_order_id)
response = send_request(payload, api_url)
handle_response(response)
end
private
def build_create_payload(amount, merch_order_id, timeout)
base_params = {
method: 'kbz.payment.precreate',
timestamp: Time.now.utc.to_i.to_s,
nonce_str: SecureRandom.hex(16),
notify_url: @notify_url,
sign_type: 'SHA256',
version: '1.0',
biz_content: {
appid: @app_id,
merch_code: @payment_method.merchant_account_id,
merch_order_id: merch_order_id,
trade_type: 'PAY_BY_QRCODE',
total_amount: amount.to_s,
trans_currency: 'MMK',
timeout_express: timeout
}.compact
}
flattened = flatten_hash(base_params)
base_params.merge(sign: generate_signature(flattened))
end
def build_close_payload(merch_order_id)
base_params = {
method: 'kbz.payment.closeorder',
timestamp: Time.now.utc.to_i.to_s,
nonce_str: SecureRandom.hex(16),
sign_type: 'SHA256',
version: '3.0',
biz_content: {
appid: @app_id,
merch_code: @payment_method.merchant_account_id,
merch_order_id: merch_order_id
}.compact
}
flattened = flatten_hash(base_params)
base_params.merge(sign: generate_signature(flattened))
end
def build_query_payload(merch_order_id)
base_params = {
method: 'kbz.payment.queryorder',
timestamp: Time.now.utc.to_i.to_s,
nonce_str: SecureRandom.hex(16),
sign_type: 'SHA256',
version: '3.0',
biz_content: {
appid: @app_id,
merch_code: @payment_method.merchant_account_id,
merch_order_id: merch_order_id
}.compact
}
flattened = flatten_hash(base_params)
base_params.merge(sign: generate_signature(flattened))
end
def flatten_hash(hash, parent_key = nil)
hash.each_with_object({}) do |(k, v), res|
key = parent_key ? "#{k}" : k.to_s
if v.is_a?(Hash)
res.merge!(flatten_hash(v, key))
else
res[key] = v.to_s
end
end
end
def generate_signature(flattened_params)
sorted_params = flattened_params.except('sign', 'sign_type').sort
string_a = sorted_params.map { |k, v| "#{k}=#{v}" }.join('&')
puts "String a: #{string_a}"
string_to_sign = "#{string_a}&key=#{@payment_method.auth_token}"
puts "String to sign: #{string_to_sign}"
Digest::SHA256.hexdigest(string_to_sign).upcase
end
def send_request(payload, url)
headers = {
'Content-Type' => 'application/json',
'User-Agent' => 'KBZPay/1.0'
}
puts "Headers: #{headers}"
puts "Payload: #{payload.to_json}"
begin
response = HTTParty.post(
url,
headers: headers,
body: { Request: payload }.to_json,
timeout: 15
)
Rails.logger.info "Response: #{response}"
JSON.parse(response.body)
rescue HTTParty::Error => e
{
error: true,
code: 'http_error',
message: "HTTP error: #{e.message}",
alert: "Payment service unavailable. Please try again later."
}
rescue SocketError => e
{
error: true,
code: 'network_error',
message: "Network error: #{e.message}",
alert: "Network connection failed. Please check your internet."
}
rescue JSON::ParserError => e
{
error: true,
code: 'invalid_response',
message: "Invalid response format: #{e.message}",
alert: "Received invalid payment response. Please contact support."
}
rescue StandardError => e
{
error: true,
code: 'unexpected_error',
message: "Unexpected error: #{e.message}",
alert: "An unexpected error occurred. Please try again."
}
end
end
def handle_response(response)
if response['error']
{
status: 'error',
code: response['code'],
message: response['message']
}
elsif response.dig('Response', 'result') == 'SUCCESS'
{
status: 'success',
data: response['Response']
}
else
error_code = response.dig('Response', 'code')
error_message = response.dig('Response', 'msg')
case error_code
when 'OrderCenter.FAILED_CREATE_ORDER_FOR_DUPLICATED_MERCHANT_ORDER_ID'
{
status: 'failed',
code: error_code,
message: 'Duplicate order ID detected. Please use a unique reference ID.'
}
else
{
status: 'failed',
code: error_code || 'unknown_error',
message: error_message || 'Payment processing failed'
}
end
end
end
end