solidus_mp 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/README.md +0 -73
- data/app/models/solidus_mp/card_payment_source.rb +23 -0
- data/app/models/solidus_mp/mp_card.rb +69 -0
- data/app/models/solidus_mp/mp_pix.rb +65 -0
- data/app/models/solidus_mp/pix_payment_source.rb +23 -0
- data/db/migrate/20231205142655_create_solidus_mp_pix_payment_sources.rb +15 -0
- data/db/migrate/20231205142931_create_solidus_mp_card_payment_sources.rb +21 -0
- data/lib/generators/solidus_mp/install/install_generator.rb +13 -0
- data/lib/generators/solidus_mp/install/templates/app/javascript/controllers/credit_card_controller.js +220 -0
- data/lib/generators/solidus_mp/install/templates/app/javascript/controllers/three_ds_controller.js +40 -0
- data/lib/generators/solidus_mp/install/templates/app/views/checkouts/confirm/_mercado_pago_card.html.erb +2 -0
- data/lib/generators/solidus_mp/install/templates/app/views/checkouts/confirm/_mercado_pago_pix.html.erb +29 -0
- data/lib/generators/solidus_mp/install/templates/app/views/checkouts/payment/_mercado_pago_card.html.erb +51 -0
- data/lib/generators/solidus_mp/install/templates/app/views/checkouts/payment/_mercado_pago_pix.html.erb +2 -0
- data/lib/solidus_mp/engine.rb +5 -0
- data/lib/solidus_mp/version.rb +1 -1
- data/lib/solidus_mp.rb +1 -0
- data/solidus_mp.gemspec +5 -5
- data/spec/models/solidus_mp/card_payment_source_spec.rb +7 -0
- data/spec/models/solidus_mp/pix_payment_source_spec.rb +7 -0
- metadata +25 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa21ea1f258cffe8857cf595e61fcb222a9ca1a144919e93890aa785cbed5427
|
4
|
+
data.tar.gz: d4db2b7c782bf3eaf93834b26354c52a70f465112f8c23eb01f046204caa60e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5057b3b6735ca61db77b199c3a1d1ddd1bd086b069aa26e060e94b82d53e7be61b48cdbc4287ddcfedfd8a9977f8df8782f3aad9c57a50e6c604a99c8381f41
|
7
|
+
data.tar.gz: 033f3e6624c5f9f22b2b36db894676002fc1f5843b62f93e774095c93a6dce30137b129471db44e2d5dcf2bfe5cc1238b0db0231f1ec87978e013cc78540cb40
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,73 +0,0 @@
|
|
1
|
-
# Solidus Mp
|
2
|
-
|
3
|
-
[![CircleCI](https://circleci.com/gh/solidusio-contrib/solidus_mp.svg?style=shield)](https://circleci.com/gh/solidusio-contrib/solidus_mp)
|
4
|
-
[![codecov](https://codecov.io/gh/solidusio-contrib/solidus_mp/branch/master/graph/badge.svg)](https://codecov.io/gh/solidusio-contrib/solidus_mp)
|
5
|
-
|
6
|
-
<!-- Explain what your extension does. -->
|
7
|
-
|
8
|
-
## Installation
|
9
|
-
|
10
|
-
Add solidus_mp to your Gemfile:
|
11
|
-
|
12
|
-
```ruby
|
13
|
-
gem 'solidus_mp'
|
14
|
-
```
|
15
|
-
|
16
|
-
Bundle your dependencies and run the installation generator:
|
17
|
-
|
18
|
-
```shell
|
19
|
-
bin/rails generate solidus_mp:install
|
20
|
-
```
|
21
|
-
|
22
|
-
## Usage
|
23
|
-
|
24
|
-
<!-- Explain how to use your extension once it's been installed. -->
|
25
|
-
|
26
|
-
## Development
|
27
|
-
|
28
|
-
### Testing the extension
|
29
|
-
|
30
|
-
First bundle your dependencies, then run `bin/rake`. `bin/rake` will default to building the dummy
|
31
|
-
app if it does not exist, then it will run specs. The dummy app can be regenerated by using
|
32
|
-
`bin/rake extension:test_app`.
|
33
|
-
|
34
|
-
```shell
|
35
|
-
bin/rake
|
36
|
-
```
|
37
|
-
|
38
|
-
To run [Rubocop](https://github.com/bbatsov/rubocop) static code analysis run
|
39
|
-
|
40
|
-
```shell
|
41
|
-
bundle exec rubocop
|
42
|
-
```
|
43
|
-
|
44
|
-
When testing your application's integration with this extension you may use its factories.
|
45
|
-
You can load Solidus core factories along with this extension's factories using this statement:
|
46
|
-
|
47
|
-
```ruby
|
48
|
-
SolidusDevSupport::TestingSupport::Factories.load_for(SolidusMp::Engine)
|
49
|
-
```
|
50
|
-
|
51
|
-
### Running the sandbox
|
52
|
-
|
53
|
-
To run this extension in a sandboxed Solidus application, you can run `bin/sandbox`. The path for
|
54
|
-
the sandbox app is `./sandbox` and `bin/rails` will forward any Rails commands to
|
55
|
-
`sandbox/bin/rails`.
|
56
|
-
|
57
|
-
Here's an example:
|
58
|
-
|
59
|
-
```
|
60
|
-
$ bin/rails server
|
61
|
-
=> Booting Puma
|
62
|
-
=> Rails 6.0.2.1 application starting in development
|
63
|
-
* Listening on tcp://127.0.0.1:3000
|
64
|
-
Use Ctrl-C to stop
|
65
|
-
```
|
66
|
-
|
67
|
-
### Releasing new versions
|
68
|
-
|
69
|
-
Please refer to the [dedicated page](https://github.com/solidusio/solidus/wiki/How-to-release-extensions) in the Solidus wiki.
|
70
|
-
|
71
|
-
## License
|
72
|
-
|
73
|
-
Copyright (c) 2023 ulysses-bull, released under the New BSD License.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SolidusMp
|
2
|
+
|
3
|
+
class CardPaymentSource < Spree::PaymentSource
|
4
|
+
|
5
|
+
def actions
|
6
|
+
%w(capture void credit)
|
7
|
+
end
|
8
|
+
|
9
|
+
def can_capture?(payment)
|
10
|
+
payment.pending? || payment.checkout?
|
11
|
+
end
|
12
|
+
|
13
|
+
def can_void?(payment)
|
14
|
+
payment.can_void?
|
15
|
+
end
|
16
|
+
|
17
|
+
def can_credit?(payment)
|
18
|
+
payment.completed? && payment.credit_allowed > 0
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module SolidusMp
|
2
|
+
class MpCard < Spree::PaymentMethod
|
3
|
+
|
4
|
+
preference :public_key, :string
|
5
|
+
preference :access_token, :string
|
6
|
+
preference :callback_url, :string
|
7
|
+
|
8
|
+
def payment_source_class
|
9
|
+
CardPaymentSource
|
10
|
+
end
|
11
|
+
|
12
|
+
def gateway_class
|
13
|
+
"MpCard"
|
14
|
+
end
|
15
|
+
|
16
|
+
def supports?(source)
|
17
|
+
source.is_a?(payment_source_class)
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_api_payment payment
|
21
|
+
begin
|
22
|
+
MpApi.configuration.access_token = preferences[:access_token]
|
23
|
+
mp_payment = MpApi::Payment.new(amount: payment.amount.to_f, payment_method: payment.source.credit_card_brand, payer_email: payment.source.email, payer_identification_type: payment.source.payer_identification_type, payer_identification_number: payment.source.tax_id, token: payment.source.token, issuer_id: payment.source.issuer_id, installments: payment.source.installments, three_d_secure_mode: true).create
|
24
|
+
payment.source.update(external_id: mp_payment.id, three_ds_url: mp_payment.three_ds_info_external_resource_url, three_ds_creq: mp_payment.three_ds_info_creq)
|
25
|
+
rescue MpApi::TooManyRequestsError
|
26
|
+
payment.invalidate
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def authorize(money, source, options = {})
|
31
|
+
purchase(money, source, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def purchase(money, source, options = {})
|
35
|
+
MpApi.configuration.access_token = preferences[:access_token]
|
36
|
+
mp_payment = MpApi::Payment.find_by_id(source.external_id)
|
37
|
+
10.times do
|
38
|
+
break if ["approved", "rejected"].include? mp_payment.status
|
39
|
+
mp_payment = MpApi::Payment.find_by_id(source.external_id)
|
40
|
+
end
|
41
|
+
if mp_payment.status == "approved"
|
42
|
+
successful_response("Transaction Accredited", mp_payment.id)
|
43
|
+
else
|
44
|
+
failure_response(mp_payment.status_detail || mp_payment.status)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def partial_name
|
49
|
+
'mercado_pago_card'
|
50
|
+
end
|
51
|
+
|
52
|
+
def successful_response message, transaction_id
|
53
|
+
ActiveMerchant::Billing::Response.new(
|
54
|
+
true,
|
55
|
+
message,
|
56
|
+
{},
|
57
|
+
authorization: transaction_id
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
def failure_response message
|
62
|
+
ActiveMerchant::Billing::Response.new(
|
63
|
+
false,
|
64
|
+
message
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module SolidusMp
|
2
|
+
class MpPix < Spree::PaymentMethod
|
3
|
+
|
4
|
+
preference :public_key, :string
|
5
|
+
preference :access_token, :string
|
6
|
+
preference :callback_url, :string
|
7
|
+
|
8
|
+
def payment_source_class
|
9
|
+
PixPaymentSource
|
10
|
+
end
|
11
|
+
|
12
|
+
def gateway_class
|
13
|
+
"MpPix"
|
14
|
+
end
|
15
|
+
|
16
|
+
def supports?(source)
|
17
|
+
source.is_a?(payment_source_class)
|
18
|
+
end
|
19
|
+
|
20
|
+
def create_api_payment payment
|
21
|
+
begin
|
22
|
+
MpApi.configuration.access_token = preferences[:access_token]
|
23
|
+
mp_payment = MpApi::Payment.new(payer_email: payment.source.email, payer_identification_type: "CPF", payment_method: "pix", payer_identification_number: payment.source.tax_id, amount: payment.amount.to_f).create
|
24
|
+
payment.source.update(external_id: mp_payment.id, qr_code: mp_payment.qr_code, qr_code_base64: mp_payment.qr_code_base_64, ticket_url: mp_payment.ticket_url)
|
25
|
+
rescue MpApi::TooManyRequestsError
|
26
|
+
payment.invalidate
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def authorize(money, source, options = {})
|
31
|
+
purchase(money, source, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def purchase(money, source, options = {})
|
35
|
+
MpApi.configuration.access_token = preferences[:access_token]
|
36
|
+
mp_payment = MpApi::Payment.find_by_id(source.external_id)
|
37
|
+
if mp_payment.status == "approved"
|
38
|
+
successful_response("Transaction Accredited", mp_payment.id)
|
39
|
+
else
|
40
|
+
failure_response(mp_payment.status_detail || mp_payment.status)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def partial_name
|
45
|
+
'mercado_pago_pix'
|
46
|
+
end
|
47
|
+
|
48
|
+
def successful_response message, transaction_id
|
49
|
+
ActiveMerchant::Billing::Response.new(
|
50
|
+
true,
|
51
|
+
message,
|
52
|
+
{},
|
53
|
+
authorization: transaction_id
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def failure_response message
|
58
|
+
ActiveMerchant::Billing::Response.new(
|
59
|
+
false,
|
60
|
+
message
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SolidusMp
|
2
|
+
|
3
|
+
class PixPaymentSource < Spree::PaymentSource
|
4
|
+
|
5
|
+
def actions
|
6
|
+
%w(capture void credit)
|
7
|
+
end
|
8
|
+
|
9
|
+
def can_capture?(payment)
|
10
|
+
payment.pending? || payment.checkout?
|
11
|
+
end
|
12
|
+
|
13
|
+
def can_void?(payment)
|
14
|
+
payment.can_void?
|
15
|
+
end
|
16
|
+
|
17
|
+
def can_credit?(payment)
|
18
|
+
payment.completed? && payment.credit_allowed > 0
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateSolidusMpPixPaymentSources < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :solidus_mp_pix_payment_sources do |t|
|
4
|
+
t.string :external_id
|
5
|
+
t.string :email
|
6
|
+
t.string :tax_id
|
7
|
+
t.integer :payment_method_id
|
8
|
+
t.string :qr_code
|
9
|
+
t.text :qr_code_base64
|
10
|
+
t.string :ticket_url
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CreateSolidusMpCardPaymentSources < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :solidus_mp_card_payment_sources do |t|
|
4
|
+
t.string :external_id
|
5
|
+
t.string :payer_identification_type
|
6
|
+
t.string :tax_id
|
7
|
+
t.string :email
|
8
|
+
t.string :card_holder_name
|
9
|
+
t.string :issuer_id
|
10
|
+
t.integer :installments
|
11
|
+
t.string :token
|
12
|
+
t.string :three_ds_mode
|
13
|
+
t.string :three_ds_url
|
14
|
+
t.string :three_ds_creq
|
15
|
+
t.string :credit_card_brand
|
16
|
+
t.integer :payment_method_id
|
17
|
+
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -10,6 +10,10 @@ module SolidusMp
|
|
10
10
|
true
|
11
11
|
end
|
12
12
|
|
13
|
+
def pin_mp_js
|
14
|
+
run 'bin/importmap pin @mercadopago/sdk-js'
|
15
|
+
end
|
16
|
+
|
13
17
|
def copy_initializer
|
14
18
|
template 'initializer.rb', 'config/initializers/solidus_mp.rb'
|
15
19
|
end
|
@@ -18,6 +22,15 @@ module SolidusMp
|
|
18
22
|
run 'bin/rails railties:install:migrations FROM=solidus_mp'
|
19
23
|
end
|
20
24
|
|
25
|
+
def install_mp_api
|
26
|
+
puts 'Installing mp_api gem...'
|
27
|
+
run 'bin/rails g mp_api:install'
|
28
|
+
end
|
29
|
+
|
30
|
+
def copy_app_files
|
31
|
+
directory 'app', 'app'
|
32
|
+
end
|
33
|
+
|
21
34
|
def run_migrations
|
22
35
|
run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]')) # rubocop:disable Layout/LineLength
|
23
36
|
if run_migrations
|
@@ -0,0 +1,220 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import { loadMercadoPago } from "@mercadopago/sdk-js"
|
3
|
+
|
4
|
+
// Connects to data-controller="credit-card"
|
5
|
+
export default class extends Controller {
|
6
|
+
|
7
|
+
static values = { publicKey: String }
|
8
|
+
static targets = ["formCardNumber", "formExpirationDate", "formSecurityCode", "formIssuer", "formInstallments", "paymentMethodId", "paymentMethodThumbnail", "formDocumentType", "formCardHolderName"]
|
9
|
+
|
10
|
+
// Anytime the controller is connected to the DOM
|
11
|
+
async connect() {
|
12
|
+
await loadMercadoPago()
|
13
|
+
this.mp = new window.MercadoPago('TEST-dc17baa6-3344-4382-af3f-15d37f3ce4ae')
|
14
|
+
// estilos dos iframes
|
15
|
+
const styles = {
|
16
|
+
style: {
|
17
|
+
height: "1.5rem",
|
18
|
+
padding: "0px",
|
19
|
+
margin: "0px",
|
20
|
+
fontFamily: "Inter",
|
21
|
+
placeholderColor: "#9C99AF",
|
22
|
+
fontSize: "1rem"
|
23
|
+
},
|
24
|
+
customFonts: [
|
25
|
+
{
|
26
|
+
src: "https://rsms.me/inter/inter.css",
|
27
|
+
},
|
28
|
+
]
|
29
|
+
}
|
30
|
+
|
31
|
+
// iframe numero cartao
|
32
|
+
const cardNumberElement = this.createIframe('cardNumber', "Número do cartão", styles, 'formCardNumber')
|
33
|
+
const expirationDateElement = this.createIframe('expirationDate', "MM/AA", styles, 'formExpirationDate')
|
34
|
+
const securityCodeElement = this.createIframe('securityCode', "Código de segurança", styles, 'formSecurityCode')
|
35
|
+
let currentBin
|
36
|
+
cardNumberElement.on('binChange', async (data) => {
|
37
|
+
const { bin } = data
|
38
|
+
try {
|
39
|
+
if (!bin && this.paymentMethodIdTarget.value) {
|
40
|
+
this.clearSelectsAndSetPlaceholders("Banco emissor", "Parcelas")
|
41
|
+
this.paymentMethodIdTarget.value = ""
|
42
|
+
}
|
43
|
+
|
44
|
+
if (bin && bin !== currentBin) {
|
45
|
+
const { results } = await this.mp.getPaymentMethods({ bin })
|
46
|
+
const paymentMethod = results[0]
|
47
|
+
this.paymentMethodIdTarget.value = paymentMethod.id
|
48
|
+
this.paymentMethodThumbnailTarget.setAttribute('src', paymentMethod.secure_thumbnail)
|
49
|
+
this.updatePCIFieldsSettings(paymentMethod, cardNumberElement, securityCodeElement)
|
50
|
+
this.updateIssuer(paymentMethod, bin)
|
51
|
+
this.updateInstallments(bin)
|
52
|
+
}
|
53
|
+
|
54
|
+
currentBin = bin
|
55
|
+
} catch (e) {
|
56
|
+
console.error('error getting payment methods: ', e)
|
57
|
+
}
|
58
|
+
})
|
59
|
+
|
60
|
+
cardNumberElement.on('focus', this.onFocus(this.formCardNumberTarget))
|
61
|
+
cardNumberElement.on('blur', this.onBlur(this.formCardNumberTarget))
|
62
|
+
|
63
|
+
expirationDateElement.on('focus', this.onFocus(this.formExpirationDateTarget))
|
64
|
+
expirationDateElement.on('blur', this.onBlur(this.formExpirationDateTarget))
|
65
|
+
|
66
|
+
securityCodeElement.on('focus', this.onFocus(this.formSecurityCodeTarget))
|
67
|
+
securityCodeElement.on('blur', this.onBlur(this.formSecurityCodeTarget))
|
68
|
+
await this.getIdentificationTypes()
|
69
|
+
|
70
|
+
document.getElementById('checkout_form_payment').addEventListener('submit', e => {
|
71
|
+
if (this.formCardHolderNameTarget.value === "") return
|
72
|
+
this.createCardToken(e, this.mp)
|
73
|
+
})
|
74
|
+
|
75
|
+
}
|
76
|
+
|
77
|
+
createIframe(inputName, placeholder, Styles, divId){
|
78
|
+
const element = this.mp.fields.create(inputName, {
|
79
|
+
placeholder: placeholder,
|
80
|
+
...Styles
|
81
|
+
}).mount(divId)
|
82
|
+
return element
|
83
|
+
}
|
84
|
+
|
85
|
+
onFocus(divElement) {
|
86
|
+
return function (event) {
|
87
|
+
divElement.setAttribute('data-focus', 'true')
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
onBlur(divElement) {
|
92
|
+
return function (event) {
|
93
|
+
divElement.removeAttribute('data-focus')
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
async getIdentificationTypes() {
|
98
|
+
try {
|
99
|
+
const identificationTypes = await this.mp.getIdentificationTypes()
|
100
|
+
|
101
|
+
this.createSelectOptions(this.formDocumentTypeTarget, identificationTypes)
|
102
|
+
} catch (e) {
|
103
|
+
return console.error('Error getting identificationTypes: ', e)
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
createSelectOptions(elem, options, labelsAndKeys = { label: "name", value: "id" }) {
|
108
|
+
const { label, value } = labelsAndKeys
|
109
|
+
|
110
|
+
elem.options.length = 0
|
111
|
+
|
112
|
+
const tempOptions = document.createDocumentFragment()
|
113
|
+
|
114
|
+
options.forEach(option => {
|
115
|
+
const optValue = option[value]
|
116
|
+
const optLabel = option[label]
|
117
|
+
|
118
|
+
const opt = document.createElement('option')
|
119
|
+
opt.value = optValue
|
120
|
+
opt.textContent = optLabel
|
121
|
+
|
122
|
+
tempOptions.appendChild(opt)
|
123
|
+
})
|
124
|
+
|
125
|
+
elem.appendChild(tempOptions)
|
126
|
+
}
|
127
|
+
|
128
|
+
clearSelectsAndSetPlaceholders(issuerPlaceholder, installmentsPlaceholder) {
|
129
|
+
clearHTMLSelectChildrenFrom(this.formIssuerTarget)
|
130
|
+
createSelectElementPlaceholder(this.formIssuerTarget, issuerPlaceholder)
|
131
|
+
|
132
|
+
clearHTMLSelectChildrenFrom(this.formInstallmentsTarget)
|
133
|
+
createSelectElementPlaceholder(this.formInstallmentsTarget, installmentsPlaceholder)
|
134
|
+
}
|
135
|
+
|
136
|
+
clearHTMLSelectChildrenFrom(element) {
|
137
|
+
const currOptions = [...element.children]
|
138
|
+
currOptions.forEach(child => child.remove())
|
139
|
+
}
|
140
|
+
|
141
|
+
createSelectElementPlaceholder(element, placeholder) {
|
142
|
+
const optionElement = document.createElement('option')
|
143
|
+
optionElement.textContent = placeholder
|
144
|
+
optionElement.setAttribute('selected', "")
|
145
|
+
optionElement.setAttribute('disabled', "")
|
146
|
+
|
147
|
+
element.appendChild(optionElement)
|
148
|
+
}
|
149
|
+
|
150
|
+
updatePCIFieldsSettings(paymentMethod, cardNumberElement, securityCodeElement) {
|
151
|
+
const { settings } = paymentMethod
|
152
|
+
|
153
|
+
const cardNumberSettings = settings[0].card_number
|
154
|
+
cardNumberElement.update({
|
155
|
+
settings: cardNumberSettings
|
156
|
+
})
|
157
|
+
|
158
|
+
const securityCodeSettings = settings[0].security_code
|
159
|
+
securityCodeElement.update({
|
160
|
+
settings: securityCodeSettings
|
161
|
+
})
|
162
|
+
}
|
163
|
+
|
164
|
+
async updateIssuer(paymentMethod, bin) {
|
165
|
+
const { additional_info_needed, issuer } = paymentMethod
|
166
|
+
let issuerOptions = [issuer]
|
167
|
+
|
168
|
+
if (additional_info_needed.includes('issuer_id')) {
|
169
|
+
issuerOptions = await getIssuers(paymentMethod, bin)
|
170
|
+
}
|
171
|
+
|
172
|
+
this.createSelectOptions(this.formIssuerTarget, issuerOptions)
|
173
|
+
}
|
174
|
+
|
175
|
+
async getIssuers(paymentMethod, bin) {
|
176
|
+
try {
|
177
|
+
const { id: paymentMethodId } = paymentMethod
|
178
|
+
return await this.mp.getIssuers({ paymentMethodId, bin })
|
179
|
+
} catch (e) {
|
180
|
+
console.error('error getting issuers: ', e)
|
181
|
+
}
|
182
|
+
}
|
183
|
+
|
184
|
+
async updateInstallments(bin) {
|
185
|
+
try {
|
186
|
+
const installments = await this.mp.getInstallments({
|
187
|
+
amount: document.getElementById('transactionAmount').value,
|
188
|
+
bin,
|
189
|
+
paymentTypeId: 'credit_card'
|
190
|
+
});
|
191
|
+
const installmentOptions = installments[0].payer_costs;
|
192
|
+
const installmentOptionsKeys = { label: 'recommended_message', value: 'installments' };
|
193
|
+
this.createSelectOptions(this.formInstallmentsTarget, installmentOptions, installmentOptionsKeys);
|
194
|
+
} catch (error) {
|
195
|
+
console.error('error getting installments: ', error)
|
196
|
+
}
|
197
|
+
}
|
198
|
+
|
199
|
+
async createCardToken(event, mp) {
|
200
|
+
try {
|
201
|
+
const tokenElement = document.getElementById('token');
|
202
|
+
if (!tokenElement.value) {
|
203
|
+
const formElement = document.getElementById('checkout_form_payment');
|
204
|
+
event.preventDefault();
|
205
|
+
const token = await mp.fields.createCardToken({
|
206
|
+
cardholderName: document.getElementById('formCardHolderName').value,
|
207
|
+
identificationType: document.getElementById('formDocumentType').value,
|
208
|
+
identificationNumber: document.getElementById('tax_id').value,
|
209
|
+
});
|
210
|
+
tokenElement.value = token.id;
|
211
|
+
formElement.submit();
|
212
|
+
} else {
|
213
|
+
formElement.submit();
|
214
|
+
}
|
215
|
+
} catch (e) {
|
216
|
+
console.error('error creating card token: ', e)
|
217
|
+
}
|
218
|
+
}
|
219
|
+
|
220
|
+
}
|
data/lib/generators/solidus_mp/install/templates/app/javascript/controllers/three_ds_controller.js
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
// Connects to data-controller="credit-card"
|
4
|
+
export default class extends Controller {
|
5
|
+
|
6
|
+
static values = { threeDsUrl: String, threeDsCreq: String }
|
7
|
+
|
8
|
+
// Anytime the controller is connected to the DOM
|
9
|
+
async connect() {
|
10
|
+
if (!this.threeDsUrlValue) return
|
11
|
+
var iframe = document.createElement('iframe')
|
12
|
+
iframe.height = "460px"
|
13
|
+
iframe.width = "500px"
|
14
|
+
iframe.name = "myframe"
|
15
|
+
iframe.id = "myframe"
|
16
|
+
this.element.appendChild(iframe)
|
17
|
+
var idocument = iframe.contentWindow.document
|
18
|
+
var myform = idocument.createElement("form")
|
19
|
+
myform.name = "myform"
|
20
|
+
myform.setAttribute("target", "myframe")
|
21
|
+
myform.setAttribute("method", "post")
|
22
|
+
myform.setAttribute("action", this.threeDsUrlValue)
|
23
|
+
var hiddenField = idocument.createElement("input")
|
24
|
+
hiddenField.setAttribute("type", "hidden")
|
25
|
+
hiddenField.setAttribute("name", "creq")
|
26
|
+
hiddenField.setAttribute("value", this.threeDsCreqValue)
|
27
|
+
myform.appendChild(hiddenField)
|
28
|
+
iframe.appendChild(myform)
|
29
|
+
var submit_form = document.getElementById("checkout_confirm_button")
|
30
|
+
submit_form.setAttribute("class", "hidden")
|
31
|
+
myform.submit()
|
32
|
+
window.addEventListener("message", (e) => {
|
33
|
+
if (e.data.status === "COMPLETE") {
|
34
|
+
document.getElementById("checkout_form_confirm").submit();
|
35
|
+
}
|
36
|
+
})
|
37
|
+
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<div class="flex justify-between flex-col sm:flex-row gap-5">
|
2
|
+
<div>
|
3
|
+
<h2 class="text-3xl text-green-700"><%= number_to_currency(@order.total) %></h2>
|
4
|
+
<a href="<%= payment_source&.ticket_url %>" target="_blank" class=" text-indigo-800 hover:text-indigo-600">Ver no Mercado Pago→</a>
|
5
|
+
|
6
|
+
<div>
|
7
|
+
<div>
|
8
|
+
<div class=" relative mt-2 inline-block items-center hover:text-indigo-600">
|
9
|
+
<input type="text" class="rounded-md border-0 py-1.5 pr-14 shadow-sm ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6" value="<%= payment_source&.qr_code %>" disabled />
|
10
|
+
<div class="absolute inset-y-0 right-0 flex py-1.5 pr-1.5 ">
|
11
|
+
<div class=" w-9">
|
12
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="h-full">
|
13
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 7.5V6.108c0-1.135.845-2.098 1.976-2.192.373-.03.748-.057 1.123-.08M15.75 18H18a2.25 2.25 0 002.25-2.25V6.108c0-1.135-.845-2.098-1.976-2.192a48.424 48.424 0 00-1.123-.08M15.75 18.75v-1.875a3.375 3.375 0 00-3.375-3.375h-1.5a1.125 1.125 0 01-1.125-1.125v-1.5A3.375 3.375 0 006.375 7.5H5.25m11.9-3.664A2.251 2.251 0 0015 2.25h-1.5a2.251 2.251 0 00-2.15 1.586m5.8 0c.065.21.1.433.1.664v.75h-6V4.5c0-.231.035-.454.1-.664M6.75 7.5H4.875c-.621 0-1.125.504-1.125 1.125v12c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V16.5a9 9 0 00-9-9z" />
|
14
|
+
</svg>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
</div>
|
20
|
+
<div>
|
21
|
+
<%# button_to "Atualizar status", "", method: :patch, class: "mt-4 bg-indigo-600 hover:bg-indigo-500 text-white font-bold py-2 px-4 rounded" %>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div class="flex justify-center">
|
28
|
+
<img class="w-80" src="data:image/jpeg;base64,<%= payment_source&.qr_code_base64 %>" />
|
29
|
+
</div>
|
@@ -0,0 +1,51 @@
|
|
1
|
+
<div data-controller="credit-card" >
|
2
|
+
|
3
|
+
<!-- Numero do cartao -->
|
4
|
+
<div class="relative mt-2 flex items-center">
|
5
|
+
<div data-credit-card-target="formCardNumber" id="formCardNumber" class="h-[2.125rem] pt-1 px-3 border-[#6b7280] w-full rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[focus=true]:ring-2 data-[focus=true]:ring-inset data-[focus=true]:ring-indigo-600 sm:text-sm sm:leading-6 bg-white"></div>
|
6
|
+
<div class="absolute inset-y-0 right-0 flex py-1.5 pr-1.5">
|
7
|
+
<img data-credit-card-target="paymentMethodThumbnail" id= "paymentMethodThumbnail" src="" class="inline-flex items-center rounded px-1">
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<!-- data de expiração -->
|
12
|
+
<div data-credit-card-target="formExpirationDate" id="formExpirationDate" class="h-[2.125rem] pt-1 px-3 border-[#6b7280] w-full rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[focus=true]:ring-2 data-[focus=true]:ring-inset data-[focus=true]:ring-indigo-600 sm:text-sm sm:leading-6 bg-white"></div>
|
13
|
+
|
14
|
+
<!-- codigo de seguranca -->
|
15
|
+
<div data-credit-card-target="formSecurityCode" id="formSecurityCode" class="h-[2.125rem] pt-1 px-3 border-[#6b7280] w-full rounded-md border-0 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 data-[focus=true]:ring-2 data-[focus=true]:ring-inset data-[focus=true]:ring-indigo-600 sm:text-sm sm:leading-6 bg-white"></div>
|
16
|
+
|
17
|
+
<!-- nome do titular -->
|
18
|
+
<div>
|
19
|
+
<input data-credit-card-target="formCardHolderName" id="formCardHolderName" name="payment_source[<%=payment_method.id%>][card_holder_name]" type="text" placeholder="Titular do cartão" class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6">
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<!-- Banco emissor -->
|
23
|
+
<select data-credit-card-target="formIssuer" id="formIssuer" name="payment_source[<%=payment_method.id%>][issuer_id]" class="hidden">
|
24
|
+
<option value="" disabled selected>Banco emissor</option>
|
25
|
+
</select>
|
26
|
+
|
27
|
+
<!-- Parcelas -->
|
28
|
+
<div>
|
29
|
+
<select data-credit-card-target="formInstallments" name="payment_source[<%=payment_method.id%>][installments]" id="formInstallments" class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6">
|
30
|
+
<option value="" disabled selected>Parcelas</option>
|
31
|
+
</select>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
<!-- CPF/CNPJ -->
|
35
|
+
<div class="relative rounded-md shadow-sm">
|
36
|
+
<div class="absolute inset-y-0 left-0 flex items-center">
|
37
|
+
<select data-credit-card-target="formDocumentType" name="payment_source[<%=payment_method.id%>][payer_identification_type]" id="formDocumentType" class="h-full rounded-md border-0 bg-transparent py-0 pl-3 pr-7 text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm">
|
38
|
+
</select>
|
39
|
+
</div>
|
40
|
+
<input type="text" id="tax_id" inputmode="numeric" placeholder="CPF/CNPJ" name="payment_source[<%=payment_method.id%>][tax_id]" class="block w-full rounded-md border-0 py-1.5 pl-20 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-300 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6">
|
41
|
+
</div>
|
42
|
+
|
43
|
+
<!--- Email -->
|
44
|
+
<div>
|
45
|
+
<input type="text" inputmode="numeric" placeholder="E-mail" name="payment_source[<%=payment_method.id%>][email]" class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6">
|
46
|
+
</div>
|
47
|
+
|
48
|
+
<input id="token" name="payment_source[<%=payment_method.id%>][token]" type="hidden">
|
49
|
+
<input data-credit-card-target="paymentMethodId" id="paymentMethodId" name="payment_source[<%=payment_method.id%>][credit_card_brand]" type="hidden">
|
50
|
+
<input id="transactionAmount" name="transaction_amount" type="hidden" value="<%= @order.total.to_f %>">
|
51
|
+
</div>
|
data/lib/solidus_mp/engine.rb
CHANGED
@@ -11,6 +11,11 @@ module SolidusMp
|
|
11
11
|
|
12
12
|
engine_name 'solidus_mp'
|
13
13
|
|
14
|
+
initializer "solidus_mp.add_payment_method", after: "spree.register.payment_methods" do |app|
|
15
|
+
app.config.spree.payment_methods << 'SolidusMp::MpCard'
|
16
|
+
app.config.spree.payment_methods << 'SolidusMp::MpPix'
|
17
|
+
end
|
18
|
+
|
14
19
|
# use rspec for tests
|
15
20
|
config.generators do |g|
|
16
21
|
g.test_framework :rspec
|
data/lib/solidus_mp/version.rb
CHANGED
data/lib/solidus_mp.rb
CHANGED
data/solidus_mp.gemspec
CHANGED
@@ -5,17 +5,17 @@ require_relative 'lib/solidus_mp/version'
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'solidus_mp'
|
7
7
|
spec.version = SolidusMp::VERSION
|
8
|
-
spec.authors = ['
|
9
|
-
spec.email = '
|
8
|
+
spec.authors = ['caio']
|
9
|
+
spec.email = 'caioif4@gmail.com'
|
10
10
|
|
11
11
|
spec.summary = ''
|
12
12
|
spec.description = ''
|
13
|
-
spec.homepage = 'https://github.com/
|
13
|
+
spec.homepage = 'https://github.com/CaioGarcia1/'
|
14
14
|
spec.license = 'BSD-3-Clause'
|
15
15
|
|
16
16
|
spec.metadata['homepage_uri'] = spec.homepage
|
17
|
-
spec.metadata['source_code_uri'] = 'https://github.com/
|
18
|
-
spec.metadata['changelog_uri'] = 'https://github.com/
|
17
|
+
spec.metadata['source_code_uri'] = 'https://github.com/CaioGarcia1/'
|
18
|
+
spec.metadata['changelog_uri'] = 'https://github.com/CaioGarcia1/'
|
19
19
|
|
20
20
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.5', '< 4')
|
21
21
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solidus_mp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- caio
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: solidus_core
|
@@ -73,7 +73,7 @@ dependencies:
|
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '2.7'
|
75
75
|
description: ''
|
76
|
-
email:
|
76
|
+
email: caioif4@gmail.com
|
77
77
|
executables: []
|
78
78
|
extensions: []
|
79
79
|
extra_rdoc_files: []
|
@@ -90,6 +90,10 @@ files:
|
|
90
90
|
- LICENSE
|
91
91
|
- README.md
|
92
92
|
- Rakefile
|
93
|
+
- app/models/solidus_mp/card_payment_source.rb
|
94
|
+
- app/models/solidus_mp/mp_card.rb
|
95
|
+
- app/models/solidus_mp/mp_pix.rb
|
96
|
+
- app/models/solidus_mp/pix_payment_source.rb
|
93
97
|
- bin/console
|
94
98
|
- bin/rails
|
95
99
|
- bin/rails-engine
|
@@ -99,7 +103,15 @@ files:
|
|
99
103
|
- bin/setup
|
100
104
|
- config/locales/en.yml
|
101
105
|
- config/routes.rb
|
106
|
+
- db/migrate/20231205142655_create_solidus_mp_pix_payment_sources.rb
|
107
|
+
- db/migrate/20231205142931_create_solidus_mp_card_payment_sources.rb
|
102
108
|
- lib/generators/solidus_mp/install/install_generator.rb
|
109
|
+
- lib/generators/solidus_mp/install/templates/app/javascript/controllers/credit_card_controller.js
|
110
|
+
- lib/generators/solidus_mp/install/templates/app/javascript/controllers/three_ds_controller.js
|
111
|
+
- lib/generators/solidus_mp/install/templates/app/views/checkouts/confirm/_mercado_pago_card.html.erb
|
112
|
+
- lib/generators/solidus_mp/install/templates/app/views/checkouts/confirm/_mercado_pago_pix.html.erb
|
113
|
+
- lib/generators/solidus_mp/install/templates/app/views/checkouts/payment/_mercado_pago_card.html.erb
|
114
|
+
- lib/generators/solidus_mp/install/templates/app/views/checkouts/payment/_mercado_pago_pix.html.erb
|
103
115
|
- lib/generators/solidus_mp/install/templates/initializer.rb
|
104
116
|
- lib/solidus_mp.rb
|
105
117
|
- lib/solidus_mp/configuration.rb
|
@@ -107,14 +119,16 @@ files:
|
|
107
119
|
- lib/solidus_mp/testing_support/factories.rb
|
108
120
|
- lib/solidus_mp/version.rb
|
109
121
|
- solidus_mp.gemspec
|
122
|
+
- spec/models/solidus_mp/card_payment_source_spec.rb
|
123
|
+
- spec/models/solidus_mp/pix_payment_source_spec.rb
|
110
124
|
- spec/spec_helper.rb
|
111
|
-
homepage: https://github.com/
|
125
|
+
homepage: https://github.com/CaioGarcia1/
|
112
126
|
licenses:
|
113
127
|
- BSD-3-Clause
|
114
128
|
metadata:
|
115
|
-
homepage_uri: https://github.com/
|
116
|
-
source_code_uri: https://github.com/
|
117
|
-
changelog_uri: https://github.com/
|
129
|
+
homepage_uri: https://github.com/CaioGarcia1/
|
130
|
+
source_code_uri: https://github.com/CaioGarcia1/
|
131
|
+
changelog_uri: https://github.com/CaioGarcia1/
|
118
132
|
post_install_message:
|
119
133
|
rdoc_options: []
|
120
134
|
require_paths:
|
@@ -133,9 +147,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
147
|
- !ruby/object:Gem::Version
|
134
148
|
version: '0'
|
135
149
|
requirements: []
|
136
|
-
rubygems_version: 3.4.
|
150
|
+
rubygems_version: 3.4.6
|
137
151
|
signing_key:
|
138
152
|
specification_version: 4
|
139
153
|
summary: ''
|
140
154
|
test_files:
|
155
|
+
- spec/models/solidus_mp/card_payment_source_spec.rb
|
156
|
+
- spec/models/solidus_mp/pix_payment_source_spec.rb
|
141
157
|
- spec/spec_helper.rb
|