spree_vpago 2.0.8 → 2.0.9.pre.beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -3
- data/app/assets/config/spree_vpago_manifest.js +1 -0
- data/app/assets/javascripts/vpago/vpago_payments/payment_processing_listener.js +46 -0
- data/app/controllers/spree/vpago_payments_controller.rb +1 -1
- data/app/helpers/vpago/admin/base_helper_decorator.rb +1 -0
- data/app/models/spree/gateway/true_money.rb +91 -0
- data/app/models/vpago/payment_decorator.rb +5 -1
- data/app/models/vpago/payment_method_decorator.rb +7 -1
- data/app/serializers/spree/v2/storefront/payment_serializer_decorator.rb +1 -1
- data/app/services/vpago/payment_processable.rb +1 -1
- data/app/services/vpago/payment_url_constructor.rb +12 -2
- data/app/services/vpago/rsa_handler.rb +7 -1
- data/app/views/layouts/vpago_payments.html.erb +1 -0
- data/app/views/spree/admin/payments/source_views/_true_money.html.erb +6 -0
- data/app/views/spree/vpago_payments/forms/spree/gateway/_true_money.html.erb +36 -0
- data/app/views/spree/vpago_payments/forms/spree/gateway/_vattanac_mini_app.html.erb +40 -81
- data/lib/spree_vpago/engine.rb +2 -1
- data/lib/spree_vpago/version.rb +1 -1
- data/lib/vpago/true_money/base.rb +155 -0
- data/lib/vpago/true_money/checkout.rb +34 -0
- data/lib/vpago/true_money/refund_issuer.rb +21 -0
- data/lib/vpago/true_money/transaction_status.rb +25 -0
- metadata +12 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6948c5b578075f3a30c3d6b03e19eedebc25a4fcb17b135dc5e94d4dde36101
|
4
|
+
data.tar.gz: eddcd8654c96fc90f535b56c3a2d590ac62ca6225293a3c56873f9a58f6721c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9196c2b45a53620fcc66a375d95c301b1d424133517dfe6787e183fe1795d3d91768e7015d048de79d99b905bedf01dc9a60a5db5837c0aa0e6d6c817526699
|
7
|
+
data.tar.gz: c75a4d97aa0ce177c4b7fde7c580964e35e2ea47f625dea08eaf920bae1b6916c12ba477e5aa052705fbffacfe7277cfbea4653bd2d225bd9c7c2f27b6d15530
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
spree_vpago (2.0.
|
4
|
+
spree_vpago (2.0.9.pre.beta)
|
5
5
|
faraday
|
6
6
|
google-cloud-firestore
|
7
7
|
spree_api (>= 4.5)
|
@@ -242,7 +242,7 @@ GEM
|
|
242
242
|
gapic-common (~> 1.0)
|
243
243
|
google-cloud-errors (~> 1.0)
|
244
244
|
google-logging-utils (0.2.0)
|
245
|
-
google-protobuf (4.31.1)
|
245
|
+
google-protobuf (4.31.1-arm64-darwin)
|
246
246
|
bigdecimal
|
247
247
|
rake (>= 13)
|
248
248
|
googleapis-common-protos (1.8.0)
|
@@ -259,7 +259,7 @@ GEM
|
|
259
259
|
multi_json (~> 1.11)
|
260
260
|
os (>= 0.9, < 2.0)
|
261
261
|
signet (>= 0.16, < 2.a)
|
262
|
-
grpc (1.73.0)
|
262
|
+
grpc (1.73.0-arm64-darwin)
|
263
263
|
google-protobuf (>= 3.25, < 5.0)
|
264
264
|
googleapis-common-protos-types (~> 1.0)
|
265
265
|
hashdiff (1.1.0)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
window.initPaymentProcessingListener = async function (
|
2
|
+
firebaseConfigs,
|
3
|
+
documentReferencePath,
|
4
|
+
successUrl
|
5
|
+
) {
|
6
|
+
function log(status, processing, reasonCode, reasonMessage) {
|
7
|
+
console.log(`Status: ${status}`);
|
8
|
+
console.log(`Reason Code: ${reasonCode}`);
|
9
|
+
console.log(`Processing: ${processing ? "Processing..." : "No more process."}`);
|
10
|
+
console.log(`Reason Message: ${reasonMessage}`);
|
11
|
+
}
|
12
|
+
|
13
|
+
window.listenToProcessingState({
|
14
|
+
firebaseConfigs,
|
15
|
+
documentReferencePath,
|
16
|
+
|
17
|
+
onPaymentIsProcessing(orderState, paymentState, processing, reasonCode, reasonMessage) {
|
18
|
+
log("Payment is processing", processing, reasonCode, reasonMessage);
|
19
|
+
},
|
20
|
+
|
21
|
+
onOrderIsProcessing(orderState, paymentState, processing, reasonCode, reasonMessage) {
|
22
|
+
log("Order is processing", processing, reasonCode, reasonMessage);
|
23
|
+
},
|
24
|
+
|
25
|
+
onOrderIsCompleted(orderState, paymentState, processing, reasonCode, reasonMessage) {
|
26
|
+
log("Order is completed", processing, reasonCode, reasonMessage);
|
27
|
+
},
|
28
|
+
|
29
|
+
onOrderProcessFailed(orderState, paymentState, processing, reasonCode, reasonMessage) {
|
30
|
+
log("Order process failed", processing, reasonCode, reasonMessage);
|
31
|
+
},
|
32
|
+
|
33
|
+
onPaymentIsRefunded(orderState, paymentState, processing, reasonCode, reasonMessage) {
|
34
|
+
log("Payment is refunded", processing, reasonCode, reasonMessage);
|
35
|
+
},
|
36
|
+
|
37
|
+
onPaymentProcessFailed(orderState, paymentState, processing, reasonCode, reasonMessage) {
|
38
|
+
log("Payment process failed", processing, reasonCode, reasonMessage);
|
39
|
+
},
|
40
|
+
|
41
|
+
onCompleted(orderState, paymentState, processing, reasonCode, reasonMessage) {
|
42
|
+
log("Completed — redirecting to success URL", processing, reasonCode, reasonMessage);
|
43
|
+
window.location.href = successUrl;
|
44
|
+
}
|
45
|
+
});
|
46
|
+
};
|
@@ -5,7 +5,7 @@ module Spree
|
|
5
5
|
|
6
6
|
skip_before_action :verify_authenticity_token, only: [:process_payment]
|
7
7
|
|
8
|
-
rescue_from ActiveRecord::RecordNotFound, with: :
|
8
|
+
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
9
9
|
rescue_from CanCan::AccessDenied, with: :access_denied
|
10
10
|
|
11
11
|
# GET
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Spree
|
2
|
+
class Gateway::TrueMoney < PaymentMethod
|
3
|
+
preference :check_transaction_url, :string
|
4
|
+
preference :refund_url, :string
|
5
|
+
preference :generate_payment_url, :string
|
6
|
+
preference :access_token_url, :string
|
7
|
+
|
8
|
+
preference :client_id, :string
|
9
|
+
preference :client_secret, :string
|
10
|
+
preference :private_key, :text
|
11
|
+
preference :return_url_scheme, :string
|
12
|
+
preference :android_package_name, :string
|
13
|
+
preference :redirect_type, :string
|
14
|
+
|
15
|
+
def method_type
|
16
|
+
'true_money'
|
17
|
+
end
|
18
|
+
|
19
|
+
def payment_source_class
|
20
|
+
Spree::VpagoPaymentSource
|
21
|
+
end
|
22
|
+
|
23
|
+
# force to purchase instead of authorize
|
24
|
+
def auto_capture?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
# override
|
29
|
+
# purchase is used when pre auth disabled
|
30
|
+
def purchase(_amount, _source, gateway_options = {})
|
31
|
+
_, payment_number = gateway_options[:order_id].split('-')
|
32
|
+
payment = Spree::Payment.find_by(number: payment_number)
|
33
|
+
|
34
|
+
checker = check_transaction(payment)
|
35
|
+
payment.update(transaction_response: checker.json_response)
|
36
|
+
|
37
|
+
success = checker.success?
|
38
|
+
params = {}
|
39
|
+
|
40
|
+
params[:payment_response] = payment.transaction_response
|
41
|
+
|
42
|
+
if success
|
43
|
+
ActiveMerchant::Billing::Response.new(true, 'Payway Gateway: Purchased', params)
|
44
|
+
else
|
45
|
+
ActiveMerchant::Billing::Response.new(false, 'Payway Gateway: Purchasing Failed', params)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# override
|
50
|
+
def void(_response_code, gateway_options)
|
51
|
+
_, payment_number = gateway_options[:order_id].split('-')
|
52
|
+
payment = Spree::Payment.find_by(number: payment_number)
|
53
|
+
|
54
|
+
if payment.true_money_payment?
|
55
|
+
params = {}
|
56
|
+
success, params[:refund_response] = true_money_refund(payment)
|
57
|
+
|
58
|
+
if success
|
59
|
+
ActiveMerchant::Billing::Response.new(true, 'True money Gateway: successfully canceled.', params)
|
60
|
+
else
|
61
|
+
ActiveMerchant::Billing::Response.new(false, 'True money Gateway: Failed to canceleed', params)
|
62
|
+
end
|
63
|
+
else
|
64
|
+
ActiveMerchant::Billing::Response.new(true, 'True money Gateway: Payment has been voided.')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def true_money_refund(payment)
|
69
|
+
refund_issuer = Vpago::TrueMoney::RefundIssuer.new(payment, {})
|
70
|
+
refund_issuer.call
|
71
|
+
|
72
|
+
[refund_issuer.success?, refund_issuer.parsed_response]
|
73
|
+
end
|
74
|
+
|
75
|
+
def cancel(_response_code, _payment)
|
76
|
+
# we can use this to send request to payment gateway api to cancel the payment ( void )
|
77
|
+
# currently True money does not support to cancel the gateway
|
78
|
+
|
79
|
+
# in our case don't do anything
|
80
|
+
ActiveMerchant::Billing::Response.new(true, '')
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def check_transaction(payment)
|
86
|
+
checker = Vpago::TrueMoney::TransactionStatus.new(payment)
|
87
|
+
checker.call
|
88
|
+
checker
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -5,6 +5,7 @@ module Vpago
|
|
5
5
|
base.after_create -> { Vpago::PayoutsGenerator.new(self).call }, if: :should_generate_payouts?
|
6
6
|
|
7
7
|
base.delegate :checkout_url,
|
8
|
+
:web_checkout_url,
|
8
9
|
:processing_url,
|
9
10
|
:success_url,
|
10
11
|
:process_payment_url,
|
@@ -56,10 +57,13 @@ module Vpago
|
|
56
57
|
pre_auth_status == 'CANCELLED'
|
57
58
|
end
|
58
59
|
|
60
|
+
def true_money_payment?
|
61
|
+
payment_method.type_true_money?
|
62
|
+
end
|
63
|
+
|
59
64
|
def vattanac_mini_app_payment?
|
60
65
|
payment_method.type_vattanac_mini_app?
|
61
66
|
end
|
62
|
-
|
63
67
|
end
|
64
68
|
end
|
65
69
|
|
@@ -6,6 +6,7 @@ module Vpago
|
|
6
6
|
TYPE_ACLEDA = 'Spree::Gateway::Acleda'.freeze
|
7
7
|
TYPE_ACLEDA_MOBILE = 'Spree::Gateway::AcledaMobile'.freeze
|
8
8
|
TYPE_VATTANAC_MINI_APP = 'Spree::Gateway::VattanacMiniApp'.freeze
|
9
|
+
TYPE_TRUE_MONEY = 'Spree::Gateway::TrueMoney'.freeze
|
9
10
|
|
10
11
|
def self.prepended(base)
|
11
12
|
base.preference :icon_name, :string, default: 'cheque'
|
@@ -18,7 +19,8 @@ module Vpago
|
|
18
19
|
Spree::PaymentMethod::TYPE_WINGSDK,
|
19
20
|
Spree::PaymentMethod::TYPE_ACLEDA,
|
20
21
|
Spree::PaymentMethod::TYPE_ACLEDA_MOBILE,
|
21
|
-
Spree::PaymentMethod::TYPE_VATTANAC_MINI_APP
|
22
|
+
Spree::PaymentMethod::TYPE_VATTANAC_MINI_APP,
|
23
|
+
Spree::PaymentMethod::TYPE_TRUE_MONEY
|
22
24
|
]
|
23
25
|
end
|
24
26
|
end
|
@@ -91,6 +93,10 @@ module Vpago
|
|
91
93
|
def type_vattanac_mini_app?
|
92
94
|
type == Spree::PaymentMethod::TYPE_VATTANAC_MINI_APP
|
93
95
|
end
|
96
|
+
|
97
|
+
def type_true_money?
|
98
|
+
type == Spree::PaymentMethod::TYPE_TRUE_MONEY
|
99
|
+
end
|
94
100
|
end
|
95
101
|
end
|
96
102
|
|
@@ -3,7 +3,7 @@ module Spree
|
|
3
3
|
module Storefront
|
4
4
|
module PaymentSerializerDecorator
|
5
5
|
def self.prepended(base)
|
6
|
-
base.attributes :pre_auth_status, :checkout_url, :process_payment_url
|
6
|
+
base.attributes :pre_auth_status, :checkout_url, :process_payment_url, :web_checkout_url
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -16,7 +16,7 @@ module Vpago
|
|
16
16
|
# 2. Pre-auth is enabled, ensuring funds can be released to user if processing fails.
|
17
17
|
# PaymentProcessor is usually called after payment is made, so canceling pre-auth typically works.
|
18
18
|
def can_cancel_pre_auth?
|
19
|
-
@payment.pending? || @payment.payment_method.enable_pre_auth? || @payment.vattanac_mini_app_payment?
|
19
|
+
@payment.pending? || @payment.payment_method.enable_pre_auth? || @payment.vattanac_mini_app_payment? || @payment.true_money_payment?
|
20
20
|
end
|
21
21
|
|
22
22
|
def extract_completer_failure_reason_code(error)
|
@@ -9,13 +9,23 @@ module Vpago
|
|
9
9
|
@order = payment.order
|
10
10
|
end
|
11
11
|
|
12
|
-
def checkout_url = "#{base_url}/vpago_payments/checkout?#{query}"
|
12
|
+
def checkout_url = "#{base_url}/vpago_payments/checkout?#{query}&platform=app"
|
13
|
+
def web_checkout_url = "#{base_url}/vpago_payments/checkout?#{query}&platform=web"
|
13
14
|
def processing_url = "#{base_url}/vpago_payments/processing?#{query}"
|
14
15
|
def success_url = "#{base_url}/vpago_payments/success?#{query}"
|
15
16
|
def process_payment_url = "#{base_url}/vpago_payments/process_payment?#{query}"
|
16
17
|
|
17
18
|
def query
|
18
|
-
{
|
19
|
+
{
|
20
|
+
payment_number: payment.number,
|
21
|
+
order_number: order.number,
|
22
|
+
order_jwt_token: order_jwt_token,
|
23
|
+
offsite_payment: offsite_payment? ? true : nil
|
24
|
+
}.compact.to_query
|
25
|
+
end
|
26
|
+
|
27
|
+
def offsite_payment?
|
28
|
+
payment.payment_method.type_true_money?
|
19
29
|
end
|
20
30
|
|
21
31
|
private
|
@@ -23,5 +23,11 @@ module Vpago
|
|
23
23
|
signature_bytes = Base64.decode64(signature)
|
24
24
|
public_key_object.verify(OpenSSL::Digest.new('SHA256'), signature_bytes, data)
|
25
25
|
end
|
26
|
+
|
27
|
+
def generate_signature(payload)
|
28
|
+
sign_hash = OpenSSL::Digest.new('SHA256')
|
29
|
+
private_key = OpenSSL::PKey::RSA.new(@private_key)
|
30
|
+
Base64.strict_encode64(private_key.sign(sign_hash, payload))
|
31
|
+
end
|
26
32
|
end
|
27
|
-
end
|
33
|
+
end
|
@@ -8,6 +8,7 @@
|
|
8
8
|
<%= csrf_meta_tags %>
|
9
9
|
<%= csp_meta_tag %>
|
10
10
|
|
11
|
+
<%= javascript_include_tag "vpago/vpago_payments/payment_processing_listener", 'data-turbo-track': 'reload' %>
|
11
12
|
<%= javascript_include_tag "vpago/vpago_payments/request_process_payment", 'data-turbo-track': 'reload' %>
|
12
13
|
<%= javascript_include_tag "vpago/vpago_payments/user_informers/#{user_informer}", 'data-turbo-track': 'reload' %>
|
13
14
|
</head>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<% @checkout = ::Vpago::TrueMoney::Checkout.new(@payment) %>
|
2
|
+
<% redirect_urls = @checkout.generate_payment_urls(params[:platform]) %>
|
3
|
+
<% @payment.user_informer.payment_is_processing(processing: true) %>
|
4
|
+
<h1> <%= params[:platform] %> </h1>
|
5
|
+
<script>
|
6
|
+
document.addEventListener("DOMContentLoaded", () => {
|
7
|
+
const platform = "<%= params[:platform] %>";
|
8
|
+
const redirectUrls = <%= redirect_urls.to_json.html_safe %>;
|
9
|
+
const confirmButton = document.getElementById("confirm-payment-button");
|
10
|
+
const firebaseConfigs = <%= Rails.application.credentials.firebase_web_config.to_json.html_safe %>;
|
11
|
+
const docRefPath = "<%= @payment.user_informer.document_reference_path %>";
|
12
|
+
const successUrl = "<%= @payment.success_url %>";
|
13
|
+
|
14
|
+
function setupConfirmPaymentButton() {
|
15
|
+
if (platform === "app") {
|
16
|
+
if (confirmButton) confirmButton.style.display = "none";
|
17
|
+
window.location.href = redirectUrls.webview;
|
18
|
+
window.top.location.href = redirectUrls.deeplink;
|
19
|
+
} else if (confirmButton) {
|
20
|
+
confirmButton.addEventListener("click", () => window.open(redirectUrls.webview , "_blank"));
|
21
|
+
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
function setupPaymentProcessingListener() {
|
26
|
+
if (window.initPaymentProcessingListener) {
|
27
|
+
window.initPaymentProcessingListener(firebaseConfigs, docRefPath, successUrl);
|
28
|
+
} else {
|
29
|
+
console.warn("initPaymentProcessingListener is not defined");
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
setupConfirmPaymentButton();
|
34
|
+
setupPaymentProcessingListener();
|
35
|
+
});
|
36
|
+
</script>
|
@@ -2,92 +2,51 @@
|
|
2
2
|
<% @payment.user_informer.payment_is_processing(processing: true) %>
|
3
3
|
|
4
4
|
<p id="unsupported-message"
|
5
|
-
style="margin-top: 10px;
|
6
|
-
font-weight: bold;
|
7
|
-
color: red;
|
8
|
-
display: none;">
|
5
|
+
style="margin-top: 10px; font-weight: bold; color: red; display: none;">
|
9
6
|
</p>
|
10
7
|
|
11
8
|
<script>
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
9
|
+
document.addEventListener("DOMContentLoaded", () => {
|
10
|
+
|
11
|
+
const setupMiniAppPayment = () => {
|
12
|
+
const detectMobileOS = () => {
|
13
|
+
const ua = navigator.userAgent || navigator.vendor || window.opera;
|
14
|
+
if (/iPad|iPhone|iPod/.test(ua) && !window.MSStream) return 'iOS';
|
15
|
+
if (/Android/i.test(ua)) return 'Android';
|
16
|
+
return 'unknown';
|
17
|
+
};
|
18
|
+
|
19
|
+
const os = detectMobileOS();
|
20
|
+
const payload = {
|
21
|
+
data: "<%= j @checkout.signed_payload %>",
|
22
|
+
paymentId: "<%= @checkout.payment_id %>"
|
23
|
+
};
|
24
|
+
|
25
|
+
if (os === 'iOS' && window.webkit?.messageHandlers?.startPayment) {
|
26
|
+
window.webkit.messageHandlers.startPayment.postMessage(JSON.stringify(payload));
|
27
|
+
} else if (os === 'Android' && window.AndroidInterface?.startPayment) {
|
28
|
+
window.AndroidInterface.startPayment(JSON.stringify(payload));
|
29
|
+
} else {
|
30
|
+
const unsupported = document.getElementById('unsupported-message');
|
31
|
+
unsupported.style.display = 'block';
|
32
|
+
unsupported.innerText = 'Unsupported OS';
|
33
|
+
}
|
24
34
|
};
|
25
35
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
} else {
|
31
|
-
const unsupported = document.getElementById('unsupported-message');
|
32
|
-
unsupported.style.display = 'block';
|
33
|
-
unsupported.innerText = 'Unsupported OS';
|
34
|
-
}
|
35
|
-
|
36
|
-
const firebaseConfigs = <%= Rails.application.credentials.firebase_web_config.to_json.html_safe %>;
|
37
|
-
|
38
|
-
window.listenToProcessingState({
|
39
|
-
firebaseConfigs: firebaseConfigs,
|
40
|
-
documentReferencePath: "<%= @payment.user_informer.document_reference_path %>",
|
41
|
-
|
42
|
-
onPaymentIsProcessing: function (orderState, paymentState, processing, reasonCode, reasonMessage) {
|
43
|
-
console.log("Status: Payment is processing");
|
44
|
-
console.log("Reason Code:", reasonCode);
|
45
|
-
console.log("Processing:", processing ? "Processing..." : "No more process.");
|
46
|
-
console.log("Reason Message:", reasonMessage);
|
47
|
-
},
|
48
|
-
|
49
|
-
onOrderIsProcessing: function (orderState, paymentState, processing, reasonCode, reasonMessage) {
|
50
|
-
console.log("Status: Order is processing");
|
51
|
-
console.log("Reason Code:", reasonCode);
|
52
|
-
console.log("Processing:", processing ? "Processing..." : "No more process.");
|
53
|
-
console.log("Reason Message:", reasonMessage);
|
54
|
-
},
|
36
|
+
const setupPaymentProcessingListener = () => {
|
37
|
+
const firebaseConfigs = <%= Rails.application.credentials.firebase_web_config.to_json.html_safe %>;
|
38
|
+
const documentReferencePath = "<%= @payment.user_informer.document_reference_path %>";
|
39
|
+
const successUrl = "<%= @payment.success_url %>";
|
55
40
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
console.log("
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
console.log("Processing:", processing ? "Processing..." : "No more process.");
|
67
|
-
console.log("Reason Message:", reasonMessage);
|
68
|
-
},
|
69
|
-
|
70
|
-
onPaymentIsRefunded: function (orderState, paymentState, processing, reasonCode, reasonMessage) {
|
71
|
-
console.log("Status: Payment is refunded");
|
72
|
-
console.log("Reason Code:", reasonCode);
|
73
|
-
console.log("Processing:", processing ? "Processing..." : "No more process.");
|
74
|
-
console.log("Reason Message:", reasonMessage);
|
75
|
-
},
|
76
|
-
|
77
|
-
onPaymentProcessFailed: function (orderState, paymentState, processing, reasonCode, reasonMessage) {
|
78
|
-
console.log("Status: Payment process failed");
|
79
|
-
console.log("Reason Code:", reasonCode);
|
80
|
-
console.log("Processing:", processing ? "Processing..." : "No more process.");
|
81
|
-
console.log("Reason Message:", reasonMessage);
|
82
|
-
},
|
83
|
-
|
84
|
-
onCompleted: function (orderState, paymentState, processing, reasonCode, reasonMessage) {
|
85
|
-
console.log("Status: Completed — redirecting to success URL");
|
86
|
-
console.log("Reason Code:", reasonCode);
|
87
|
-
console.log("Reason Message:", reasonMessage);
|
88
|
-
let successPath = "<%= @payment.success_url %>";
|
89
|
-
window.location.href = successPath;
|
90
|
-
},
|
91
|
-
});
|
41
|
+
if (window.initPaymentProcessingListener) {
|
42
|
+
window.initPaymentProcessingListener(firebaseConfigs, documentReferencePath, successUrl);
|
43
|
+
} else {
|
44
|
+
console.log("initPaymentProcessingListener is not defined");
|
45
|
+
}
|
46
|
+
};
|
47
|
+
|
48
|
+
setupMiniAppPayment();
|
49
|
+
setupPaymentProcessingListener();
|
50
|
+
|
92
51
|
});
|
93
52
|
</script>
|
data/lib/spree_vpago/engine.rb
CHANGED
data/lib/spree_vpago/version.rb
CHANGED
@@ -0,0 +1,155 @@
|
|
1
|
+
module Vpago
|
2
|
+
module TrueMoney
|
3
|
+
class Base
|
4
|
+
TOKEN_HEADERS = { 'Content-Type' => 'application/x-www-form-urlencoded' }.freeze
|
5
|
+
CONTENT_TYPE_JSON = 'application/json'.freeze
|
6
|
+
DEFAULT_ALGORITHM = 'rsa-sha256'.freeze
|
7
|
+
DEFAULT_KEY_VERSION = 1
|
8
|
+
|
9
|
+
def initialize(payment, options = {})
|
10
|
+
@payment = payment
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def client_id
|
15
|
+
payment_method.preferred_client_id
|
16
|
+
end
|
17
|
+
|
18
|
+
def client_secret
|
19
|
+
payment_method.preferred_client_secret
|
20
|
+
end
|
21
|
+
|
22
|
+
def private_key
|
23
|
+
payment_method.preferred_private_key
|
24
|
+
end
|
25
|
+
|
26
|
+
def redirect_type
|
27
|
+
payment_method.preferred_redirect_type
|
28
|
+
end
|
29
|
+
|
30
|
+
def external_ref_id
|
31
|
+
@payment.number
|
32
|
+
end
|
33
|
+
|
34
|
+
def service_type = '01'
|
35
|
+
def currency = 'USD'
|
36
|
+
def user_type = 'CUSTOMER'
|
37
|
+
|
38
|
+
def amount
|
39
|
+
@payment.amount
|
40
|
+
end
|
41
|
+
|
42
|
+
def timestamp
|
43
|
+
@timestamp ||= Time.now.to_i
|
44
|
+
end
|
45
|
+
|
46
|
+
def access_token
|
47
|
+
@access_token ||= fetch_access_token
|
48
|
+
end
|
49
|
+
|
50
|
+
def payload
|
51
|
+
{
|
52
|
+
service_type: service_type,
|
53
|
+
external_ref_id: external_ref_id,
|
54
|
+
amount: amount,
|
55
|
+
currency: currency,
|
56
|
+
user_type: user_type
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def signature
|
61
|
+
Vpago::RsaHandler
|
62
|
+
.new(private_key: private_key)
|
63
|
+
.generate_signature(signature_input)
|
64
|
+
end
|
65
|
+
|
66
|
+
def default_headers
|
67
|
+
{
|
68
|
+
'Client-Id' => client_id,
|
69
|
+
'Authorization' => "Bearer #{access_token}",
|
70
|
+
'Signature' => "algorithm=#{algorithm};keyVersion=#{key_version};signature=#{signature}",
|
71
|
+
'Timestamp' => timestamp.to_s,
|
72
|
+
'Content-Type' => CONTENT_TYPE_JSON
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
def check_transaction_url
|
77
|
+
"#{payment_method.preferred_check_transaction_url}/#{external_ref_id}"
|
78
|
+
end
|
79
|
+
|
80
|
+
def refund_url
|
81
|
+
payment_method.preferred_refund_url
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_payment_url
|
85
|
+
payment_method.preferred_generate_payment_url
|
86
|
+
end
|
87
|
+
|
88
|
+
def access_token_url
|
89
|
+
payment_method.preferred_access_token_url
|
90
|
+
end
|
91
|
+
|
92
|
+
def android_package_name
|
93
|
+
payment_method.preferred_android_package_name
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_json(body)
|
97
|
+
JSON.parse(body)
|
98
|
+
rescue JSON::ParserError
|
99
|
+
{}
|
100
|
+
end
|
101
|
+
|
102
|
+
def fetch_access_token
|
103
|
+
response = Faraday.post(
|
104
|
+
access_token_url,
|
105
|
+
URI.encode_www_form(
|
106
|
+
grant_type: 'client_credentials',
|
107
|
+
client_id: client_id,
|
108
|
+
client_secret: client_secret
|
109
|
+
),
|
110
|
+
TOKEN_HEADERS
|
111
|
+
)
|
112
|
+
|
113
|
+
raise "Access Token Error: #{response.status} - #{response.body}" unless response.success?
|
114
|
+
|
115
|
+
JSON.parse(response.body).fetch('access_token')
|
116
|
+
end
|
117
|
+
|
118
|
+
def return_url_scheme
|
119
|
+
payment_method.preferred_return_url_scheme
|
120
|
+
end
|
121
|
+
|
122
|
+
def return_deeplink
|
123
|
+
"#{return_url_scheme}/vpago_payments/processing?payment_number=#{external_ref_id}&order_number=#{order.number}&order_jwt_token=#{order_jwt_token}"
|
124
|
+
end
|
125
|
+
|
126
|
+
def order_jwt_token
|
127
|
+
@order_jwt_token ||= JWT.encode(jwt_payload, order.token, 'HS256')
|
128
|
+
end
|
129
|
+
|
130
|
+
def order
|
131
|
+
@payment.order
|
132
|
+
end
|
133
|
+
|
134
|
+
def jwt_payload
|
135
|
+
{ order_number: order.number, order_id: order.id }
|
136
|
+
end
|
137
|
+
|
138
|
+
def payment_method
|
139
|
+
@payment.payment_method
|
140
|
+
end
|
141
|
+
|
142
|
+
def algorithm
|
143
|
+
DEFAULT_ALGORITHM
|
144
|
+
end
|
145
|
+
|
146
|
+
def key_version
|
147
|
+
DEFAULT_KEY_VERSION
|
148
|
+
end
|
149
|
+
|
150
|
+
def signature_input
|
151
|
+
"#{timestamp}#{payload.to_json}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Vpago
|
2
|
+
module TrueMoney
|
3
|
+
class Checkout < Base
|
4
|
+
def generate_payment_urls(platform)
|
5
|
+
request_body = {
|
6
|
+
payment_info: payload.to_json,
|
7
|
+
redirectionType: redirect_type,
|
8
|
+
merchantDeepLink: return_deeplink,
|
9
|
+
merchantAndroidPackageName: android_package_name,
|
10
|
+
refererLink: @payment.processing_url
|
11
|
+
}
|
12
|
+
|
13
|
+
response = Faraday.post(generate_payment_url) do |req|
|
14
|
+
req.headers = default_headers
|
15
|
+
req.body = request_body.to_json
|
16
|
+
end
|
17
|
+
|
18
|
+
body = parse_json(response.body)
|
19
|
+
|
20
|
+
if platform == 'app'
|
21
|
+
{
|
22
|
+
webview: body['data']['webview'],
|
23
|
+
deeplink: body['data']['deeplink']
|
24
|
+
}
|
25
|
+
else
|
26
|
+
body['data']['deeplink']
|
27
|
+
end
|
28
|
+
rescue Faraday::Error, JSON::ParserError, NoMethodError => e
|
29
|
+
Rails.logger.error("Failed to generate payment URL: #{e.class} - #{e.message}")
|
30
|
+
raise
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Vpago
|
2
|
+
module TrueMoney
|
3
|
+
class RefundIssuer < Base
|
4
|
+
def call
|
5
|
+
@response = Faraday.post(refund_url, payload.to_json, default_headers)
|
6
|
+
end
|
7
|
+
|
8
|
+
def success?
|
9
|
+
parsed_response.dig('status', 'code') == '000001'
|
10
|
+
end
|
11
|
+
|
12
|
+
def payload
|
13
|
+
{ external_ref_id: external_ref_id }
|
14
|
+
end
|
15
|
+
|
16
|
+
def parsed_response
|
17
|
+
@parsed_response ||= parse_json(@response.body)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Vpago
|
2
|
+
module TrueMoney
|
3
|
+
class TransactionStatus < Base
|
4
|
+
def call
|
5
|
+
@response = Faraday.get(check_transaction_url, nil, default_headers)
|
6
|
+
end
|
7
|
+
|
8
|
+
def success?
|
9
|
+
response_code == '000001'
|
10
|
+
end
|
11
|
+
|
12
|
+
def response_code
|
13
|
+
json_response.dig('status', 'code')
|
14
|
+
end
|
15
|
+
|
16
|
+
def signature_input
|
17
|
+
timestamp.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def json_response
|
21
|
+
@json_response ||= parse_json(@response.body)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spree_vpago
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.9.pre.beta
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- You
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-07-
|
11
|
+
date: 2025-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -134,6 +134,7 @@ files:
|
|
134
134
|
- app/assets/images/backend-process.svg
|
135
135
|
- app/assets/images/vpago/payway/abapay.png
|
136
136
|
- app/assets/images/vpago/payway/cards.png
|
137
|
+
- app/assets/javascripts/vpago/vpago_payments/payment_processing_listener.js
|
137
138
|
- app/assets/javascripts/vpago/vpago_payments/request_process_payment.js
|
138
139
|
- app/assets/javascripts/vpago/vpago_payments/user_informers/firebase.js
|
139
140
|
- app/controllers/.gitkeep
|
@@ -179,6 +180,7 @@ files:
|
|
179
180
|
- app/models/spree/gateway/acleda_mobile.rb
|
180
181
|
- app/models/spree/gateway/payway.rb
|
181
182
|
- app/models/spree/gateway/payway_v2.rb
|
183
|
+
- app/models/spree/gateway/true_money.rb
|
182
184
|
- app/models/spree/gateway/vattanac_mini_app.rb
|
183
185
|
- app/models/spree/gateway/wing_sdk.rb
|
184
186
|
- app/models/spree/payout.rb
|
@@ -262,6 +264,7 @@ files:
|
|
262
264
|
- app/views/spree/admin/payments/source_views/_acleda_mobile.html.erb
|
263
265
|
- app/views/spree/admin/payments/source_views/_payment_payway.html.erb
|
264
266
|
- app/views/spree/admin/payments/source_views/_payway_v2.html.erb
|
267
|
+
- app/views/spree/admin/payments/source_views/_true_money.html.erb
|
265
268
|
- app/views/spree/admin/payments/source_views/_vattanac_mini_app.html.erb
|
266
269
|
- app/views/spree/admin/payments/source_views/_vpago_payment_tmpl.html.erb
|
267
270
|
- app/views/spree/admin/payments/source_views/_wingsdk.html.erb
|
@@ -309,6 +312,7 @@ files:
|
|
309
312
|
- app/views/spree/payway_v2_card_popups/show.html.erb
|
310
313
|
- app/views/spree/vpago_payments/checkout.html.erb
|
311
314
|
- app/views/spree/vpago_payments/forms/spree/gateway/_payway_v2.html.erb
|
315
|
+
- app/views/spree/vpago_payments/forms/spree/gateway/_true_money.html.erb
|
312
316
|
- app/views/spree/vpago_payments/forms/spree/gateway/_vattanac_mini_app.html.erb
|
313
317
|
- app/views/spree/vpago_payments/processing.html.erb
|
314
318
|
- app/views/spree/vpago_payments/success.html.erb
|
@@ -378,6 +382,10 @@ files:
|
|
378
382
|
- lib/vpago/payway_v2/pre_auth_canceler.rb
|
379
383
|
- lib/vpago/payway_v2/pre_auth_completer.rb
|
380
384
|
- lib/vpago/payway_v2/transaction_status.rb
|
385
|
+
- lib/vpago/true_money/base.rb
|
386
|
+
- lib/vpago/true_money/checkout.rb
|
387
|
+
- lib/vpago/true_money/refund_issuer.rb
|
388
|
+
- lib/vpago/true_money/transaction_status.rb
|
381
389
|
- lib/vpago/vattanac_mini_app/base.rb
|
382
390
|
- lib/vpago/vattanac_mini_app/checkout.rb
|
383
391
|
- lib/vpago/vattanac_mini_app/refund_issuer.rb
|
@@ -408,9 +416,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
408
416
|
version: 3.2.0
|
409
417
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
410
418
|
requirements:
|
411
|
-
- - "
|
419
|
+
- - ">"
|
412
420
|
- !ruby/object:Gem::Version
|
413
|
-
version:
|
421
|
+
version: 1.3.1
|
414
422
|
requirements:
|
415
423
|
- none
|
416
424
|
rubygems_version: 3.4.1
|