solidus_mp 0.0.1 → 0.0.2
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/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
|
-
[](https://circleci.com/gh/solidusio-contrib/solidus_mp)
|
4
|
-
[](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
|