khipu 1.3.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bbdd3f10aea423ebab6c1e4801ae8cd3dd0da2f1
4
+ data.tar.gz: ee8f254750eef7c4e1beb041adc251a872bbd0c2
5
+ SHA512:
6
+ metadata.gz: d4da9dff3275f4eca53ff9d835d166b26672a0dc65b5f7f71b1b35c2cf50e0774b1ea905d3c494e57dd7d2733d40a3eb71207f16f71438f060f1c801ca8a3f21
7
+ data.tar.gz: e8aeca81768df4f5519c30f466349ba3cd25d4d97dd0b084754ab6bf372712eb4b58ebe08c55f2b1ff050f55db033a0a23e77f1143fbfdf50ae11a53d567c6da
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in khipu.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,29 @@
1
+ Copyright (c) 2014, khipu SpA
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are
6
+ met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # khipu
2
+
3
+ [khipu - Yo pago, Yo cobro](https://khipu.com)
4
+
5
+ Gema Ruby para utilizar los servicios de Khipu.com
6
+
7
+ Versión Biblioteca: 1.3.0
8
+
9
+ Versión API Khipu: 1.3
10
+ Versión API de notificación: 1.2
11
+
12
+ La documentación de Khipu.com se puede ver desde aquí: [https://khipu.com/page/api](https://khipu.com/page/api)
13
+
14
+ ## Uso
15
+
16
+ Primero instalar la gema ruby
17
+
18
+ ```Bash
19
+ $ gem install khipu
20
+ ```
21
+
22
+ Luego debes incluir la gema desde tus scripts Ruby usando _require 'khipu'_
23
+
24
+ ## Operaciones
25
+
26
+ Esta biblioteca implementa los siguientes servicios de khipu:
27
+
28
+ 1. Obtener listado de bancos
29
+ 2. Crear cobros y enviarlos por mail.
30
+ 3. Crear una página de Pago.
31
+ 4. Crear una página de Pago (autenticado).
32
+ 5. Crear un pago y obtener us URL.
33
+ 6. Crear un pago y obtener us URL (autenticado).
34
+ 7. Recibir y validar la notificación de un pago.
35
+ 8. Verificar el estado de una cuenta de cobro.
36
+ 9. Verificar el estado de un pago.
37
+ 10. Marcar un pago como pagado.
38
+ 11. Marcar un cobro como expirado.
39
+ 12. Marcar un pago como rechazado.
40
+
41
+
42
+ ### 1) Obtener listado de bancos.
43
+
44
+ Este servicio entrega un mapa con el listado de los bancos disponibles para efectuar un pago a un cobrador determinado.
45
+ Cada banco tiene su identificador, un nombre, el monto mínimo que se puede transferir desde él y un mensaje con
46
+ información importante.
47
+
48
+ ```Ruby
49
+ begin
50
+ banks = Khipu.create_khipu_api(ID_DEL_COBRADOR, 'SECRET_DEL_COBRADOR').receiver_banks
51
+ rescue Khipu::ApiError => error
52
+ puts error.type
53
+ puts error.message
54
+ end
55
+ ```
56
+
57
+ ### 2) Crear cobros y enviarlos por mail.
58
+
59
+ Este servicio entrega un mapa que contiene el identificador del cobro generado así como una lista de los pagos asociados
60
+ a este cobro. Por cada pago se tiene el ID, el correo asociado y la URL en khipu para pagar.
61
+
62
+ ```Ruby
63
+ begin
64
+ service = Khipu.create_khipu_api(ID_DEL_COBRADOR, SECRET_DEL_COBRADOR)
65
+ map = service.create_email({subject: 'Un cobro desde Ruby', amount: '10', destinataries: [ {name: "John Doe", email: "john.doe@gmail.com", amount: "1000"}, {name: "Jane Dow", email: "jane.dow@gmail.com", amount: "1000"}], pay_directly: true, send_emails: true})
66
+ rescue Khipu::ApiError => error
67
+ puts error.type
68
+ puts error.message
69
+ end
70
+ ```
71
+ *Importante*: El parámetro _destinataries_ es un mapa Ruby y no un string. Internamente es convertido a un string JSON
72
+
73
+ ### 3) Crear una página de Pago.
74
+
75
+ Este ejemplo genera un archivo .html con un formulario de pago en khipu.
76
+
77
+ ```Ruby
78
+ File.open('form.html', 'w') { |file|
79
+ service = Khipu.create_html_helper(2, 'e40ac9591200b2ec9277cd1c795af82d618cf78e')
80
+ form = service.create_payment_form({subject: 'Un cobro desde Ruby', body: 'El cuerpo del cobro', amount: "1000", email: 'john.doe@gmail.com'})
81
+ file.write(form)
82
+ }
83
+ ```
84
+
85
+ ### 4) Crear una página de Pago (autenticado).
86
+
87
+ Para crear una página de pago que solo permita pagar usando una cuenta asociada a un RUT en particular debes usar el
88
+ mismo servicio del punto anterior indicando el RUT en el parámetro _payer_username_
89
+
90
+ ```Ruby
91
+ File.open('form.html', 'w') { |file|
92
+ service = Khipu.create_html_helper(2, 'e40ac9591200b2ec9277cd1c795af82d618cf78e')
93
+ form = service.create_payment_form({subject: 'Un cobro desde Ruby', body: 'El cuerpo del cobro', amount: "1000", email: 'john.doe@gmail.com', payer_username: '128723463'})
94
+ file.write(form)
95
+ }
96
+ ```
97
+
98
+ ### 5) Crear un pago y obtener su URL.
99
+
100
+ Este servicio entrega un mapa que contiene el identificador de un pago generado, su URL en khipu y la URL para iniciar
101
+ el pago desde un dispositivo móvil.
102
+
103
+ ```Ruby
104
+ begin
105
+ service = Khipu.create_khipu_api(ID_DEL_COBRADOR, SECRET_DEL_COBRADOR)
106
+ map = service.create_payment_url({subject: 'Un cobro desde Ruby', body: 'El cuerpo del cobro', amount: "1000", email: 'john.doe@gmail.com'})
107
+ rescue Khipu::ApiError => error
108
+ puts error.type
109
+ puts error.message
110
+ end
111
+ ```
112
+ ### 6) Crear un pago y obtener su URL (autenticado).
113
+
114
+ Este servicio es idéntico al anterior pero usando el parámetro _payer_username_ se fuerza que la cuenta corriente usada
115
+ para pagar debe estar asociada al RUT indicado.
116
+
117
+ ```Ruby
118
+ begin
119
+ service = Khipu.create_khipu_api(ID_DEL_COBRADOR, SECRET_DEL_COBRADOR)
120
+ map = service.create_authenticated_payment_url({subject: 'Un cobro desde Ruby', body: 'El cuerpo del cobro', amount: "1000", email: 'john.doe@gmail.com', payer_username: '128723463'})
121
+ rescue Khipu::ApiError => error
122
+ puts error.type
123
+ puts error.message
124
+ end
125
+ ```
126
+ Se puede obtener una
127
+ ### 7) Validar la notificación de un pago.
128
+
129
+ Este ejemplo contacta a khipu para validar los datos de una transacción. Para usar
130
+ este servicio no es necesario configurar el SECRET del cobrador. Se retorna true si la información del la notificación
131
+ es válida. En este ejemplo los parámetros se configuran a mano, pero en producción los datos deben obtenerse desde
132
+ el _request html_.
133
+
134
+ ```Ruby
135
+ begin
136
+ service = Khipu.create_khipu_api(ID_DEL_COBRADOR, SECRET_DEL_COBRADOR)
137
+ params = {api_version: '1.2',
138
+ notification_id: 'aq1td2jl2uen',
139
+ subject: 'Motivo de prueba',
140
+ amount: 12575,
141
+ currency: 'CLP',
142
+ transaction_id: 'FTEEE5SWWO',
143
+ payer_email: 'john.doe@gmail.com',
144
+ custom: 'Custom info',
145
+ notification_signature: 'j8kPBHaPNy3PkCh...hhLvQbenpGjA=='
146
+ }
147
+ valid = service.verify_payment_notification(params)
148
+ rescue Khipu::ApiError => error
149
+ puts error.type
150
+ puts error.message
151
+ end
152
+ ```
153
+
154
+
155
+ ### 8) Verificar el estado de una cuenta de cobro.
156
+
157
+ Este servicio permite consultar el estado de una cuenta khipu. Se devuelve un mapa que indica si esta cuenta está
158
+ habilitada para cobrar y el tipo de cuenta (desarrollo o producción).
159
+
160
+ ```Ruby
161
+ begin
162
+ service = Khipu.create_khipu_api(ID_DEL_COBRADOR, SECRET_DEL_COBRADOR)
163
+ map = service.receiver_status;
164
+ rescue Khipu::ApiError => error
165
+ puts error.type
166
+ puts error.message
167
+ end
168
+ ```
169
+
170
+ ### 9) Verificar el estado de un pago.
171
+
172
+ Este servició sirve para verificar el estado de un pago.
173
+
174
+ ```Ruby
175
+ begin
176
+ service = Khipu.create_khipu_api(ID_DEL_COBRADOR, SECRET_DEL_COBRADOR)
177
+ map = service.payment_status({payment_id: '9fnsggqqi8ho'})
178
+ rescue Khipu::ApiError => error
179
+ puts error.type
180
+ puts error.message
181
+ end
182
+ ```
183
+
184
+ ### 10) Marcar un cobro como pagado.
185
+
186
+ Este servicio permite marcar un cobro como pagado. Si el pagador paga por un método alternativo a khipu, el cobrador
187
+ puede marcar este cobro como saldado.
188
+
189
+ ```Ruby
190
+ begin
191
+ service = Khipu.create_khipu_api(ID_DEL_COBRADOR, SECRET_DEL_COBRADOR)
192
+ service.set_paid_by_receiver({payment_id: '54dhfsch6avd'});
193
+ rescue Khipu::ApiError => error
194
+ puts error.type
195
+ puts error.message
196
+ end
197
+ ```
198
+
199
+ ### 11) Marcar un cobro como expirado.
200
+
201
+ Este servicio permite adelantar la expiración del cobro. Se puede adjuntar un texto que será desplegado a la gente que
202
+ trate de ir a pagar.
203
+
204
+
205
+ ```Ruby
206
+ begin
207
+ service = Khipu.create_khipu_api(ID_DEL_COBRADOR, SECRET_DEL_COBRADOR)
208
+ service.set_bill_expired({bill_id: 'udmEe', text: 'Plazo vencido'})
209
+ rescue Khipu::ApiError => error
210
+ puts error.type
211
+ puts error.message
212
+ end
213
+ ```
214
+
215
+ ### 12) Marcar un cobro como rechazado.
216
+
217
+ Este servicio permite rechazar pago con el fin de inhabilitarlo. Permite indicar la razón por la que el pagador rechaza
218
+ saldar este pago:
219
+
220
+
221
+ ```Ruby
222
+ begin
223
+ service = Khipu.create_khipu_api(ID_DEL_COBRADOR, SECRET_DEL_COBRADOR)
224
+ service.set_rejected_by_payer({payment_id: '0pk7xfgocry4', text: 'El pago no corresponde'});
225
+ rescue Khipu::ApiError => error
226
+ puts error.type
227
+ puts error.message
228
+ end
229
+ ```
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
7
+ task :test => :spec
data/khipu.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'khipu/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "khipu"
8
+ spec.version = Khipu::VERSION
9
+ spec.authors = ["Alex Lorca"]
10
+ spec.email = ["alex.lorca@khipu.com"]
11
+ spec.description = "A wrapper for khipu's web API"
12
+ spec.summary = "A wrapper for khipu's web API"
13
+ spec.homepage = "https://github.com/khipu/lib-ruby"
14
+ spec.license = "BSD"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.required_ruby_version = ">= 1.9"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ end
@@ -0,0 +1,10 @@
1
+ module Khipu
2
+ class ApiError < StandardError
3
+ attr_accessor :type, :message
4
+
5
+ def initialize(type, message)
6
+ @type = type
7
+ @message = message
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,59 @@
1
+ module Khipu
2
+ CERT_PROD = %q{-----BEGIN CERTIFICATE-----
3
+ MIIFuDCCBSGgAwIBAgIQHCWU8WfzVpZq9iYPMgYpVzANBgkqhkiG9w0BAQUFADCB
4
+ 0DEUMBIGA1UEChMLRS1TaWduIFMuQS4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0
5
+ IE5ldHdvcmsxODA2BgNVBAsTL1Rlcm1zIG9mIHVzZSBhdCBodHRwczovL3d3dy5l
6
+ LXNpZ24uY2wvcnBhIChjKTA1MTwwOgYDVQQDEzNFLVNpZ24gU0MgQ2xhc3MgMiBD
7
+ b25zdW1lciBJbmRpdmlkdWFsIFN1YnNjcmliZXIgQ0ExHzAdBgkqhkiG9w0BCQEW
8
+ EGUtc2lnbkBlLXNpZ24uY2wwHhcNMTIxMTA3MDAwMDAwWhcNMTMxMTA3MjM1OTU5
9
+ WjCCAQgxMDAuBgNVBAsTJ1Rlcm1zIG9mIHVzZSBhdCB3d3cuZS1zaWduLmNsL3Jw
10
+ YSBDKDA0KTElMCMGA1UECxMcQXV0aGVudGljYXRlZCBieSBFLVNpZ24gUy5BLjEn
11
+ MCUGA1UECxMeTWVtYmVyLCBWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMRswGQYDVQQL
12
+ ExJEaWdpdGFsIElEIENsYXNzIDIxGDAWBgNVBAsUD1JVVCAtIDkxMjM4NDUtNDEl
13
+ MCMGA1UEAxMcUm9iZXJ0byBBbmRyZXMgT3Bhem8gR2F6bXVyaTEmMCQGCSqGSIb3
14
+ DQEJARYXcm9iZXJ0by5vcGF6b0BraGlwdS5jb20wggEiMA0GCSqGSIb3DQEBAQUA
15
+ A4IBDwAwggEKAoIBAQC4xI/f6C/5V/KsIAQY3G2ExoqIb8NoM7czRw9UNdx/OEiL
16
+ QJfqJjx1gVkcZxHqcWT6DENuf7H2LyYvzXOfeQgN5Zw8FCdIxjoF00AOIf3YDLdG
17
+ 0IXoiRuZWPh9JkbGyl/tSd/fJWSh12GQAUE0m0YYMNHGiNPqiZ3jJEUgNWKjfO2D
18
+ xMqTGrujDnA3qF/2SiBrWRj5XyZCIYumrHWKGQnvmd6uaH4SOyaPc1Jl8byqfzg6
19
+ GlHX2hWyVJ2Tc+GHxAiSm8RzUfSAfgCbUm+/8gX0uDlGA7dqaa9FIZh7RWBdN4Nn
20
+ Dd9XHEOtXnBVL6mFzut57eAwEHz+a6tv2E2yy/GJAgMBAAGjggHSMIIBzjAiBgNV
21
+ HREEGzAZoBcGCCsGAQQBwQEBoAsWCTkxMjM4NDUtNDAJBgNVHRMEAjAAMAsGA1Ud
22
+ DwQEAwIFoDCBmQYDVR0gBIGRMIGOMIGLBgtghkgBhvhFAQcXAjB8MDEGCCsGAQUF
23
+ BwIBFiVodHRwczovL3d3dy5lLXNpZ24uY2wvcmVwb3NpdG9yaW8uaHRtMEcGCCsG
24
+ AQUFBwICMDsaOUNlcnRpZmljYWRvIHBhcmEgdXNvIFRyaWJ1dGFyaW8sIENvbWVy
25
+ Y2lvLCBQYWdvcyB5IE90cm9zLjAjBgNVHRIEHDAaoBgGCCsGAQQBwQECoAwWCjk5
26
+ NTUxNzQwLUswTQYDVR0fBEYwRDBCoECgPoY8aHR0cDovL29uc2l0ZWNybC52ZXJp
27
+ c2lnbi5jb20vRVNpZ25TQUNTQ0NsYXNzMi9MYXRlc3RDUkwuY3JsMDsGCCsGAQUF
28
+ BwEBBC8wLTArBggrBgEFBQcwAYYfaHR0cDovL29uc2l0ZS1vY3NwLnZlcmlzaWdu
29
+ LmNvbTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwEQYJYIZIAYb4QgEB
30
+ BAQDAgeAMBEGCmCGSAGG+EUBBgkEAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAFiXQz
31
+ K9TrKWkJH3is2H/QI8gynHQV71kvmSUnRgPk5TdXzd6EDhbaQMC3pCdwhpKKUfFo
32
+ 0Ky8wtkagE8jdCRkpDfGpnhINfJZW9HH3gKe2zFckynPZcV5fsVM1tQzWQNBPaFf
33
+ scJQlyZrC17dCDWkJ2op67BRjp97skB1SUWtRw==
34
+ -----END CERTIFICATE-----}
35
+
36
+ CERT_DEV = %q{-----BEGIN CERTIFICATE-----
37
+ MIIDszCCApsCBFA88VcwDQYJKoZIhvcNAQEFBQAwgZ0xIDAeBgkqhkiG9w0BCQEW
38
+ EXJvYmVydG9Acm9wYXpvLmNsMQswCQYDVQQGEwJDTDERMA8GA1UECAwIU2FudGlh
39
+ Z28xCzAJBgNVBAcMAlJNMRIwEAYDVQQKDAlLaGlwdS5jb20xGDAWBgNVBAsMD0dl
40
+ cmVudGUgR2VuZXJhbDEeMBwGA1UEAwwVUm9iZXJ0byBPcGF6byBHYXptdXJpMB4X
41
+ DTEyMDgyODE2MjcwM1oXDTEzMDgyODE2MjcwM1owgZ0xIDAeBgkqhkiG9w0BCQEW
42
+ EXJvYmVydG9Acm9wYXpvLmNsMQswCQYDVQQGEwJDTDERMA8GA1UECAwIU2FudGlh
43
+ Z28xCzAJBgNVBAcMAlJNMRIwEAYDVQQKDAlLaGlwdS5jb20xGDAWBgNVBAsMD0dl
44
+ cmVudGUgR2VuZXJhbDEeMBwGA1UEAwwVUm9iZXJ0byBPcGF6byBHYXptdXJpMIIB
45
+ IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk+0IYrfbAPSRSX8u3+js786J
46
+ QTMXGS52LUFHOKXokQbqfcbPs9qwBDr4NeRbM3GQ8CRLLM9PKenHbd93ErpY4GDC
47
+ CWZPU9RA4nFgJa/pJzFEf74dR0tQOnEdEDwCdlAp7EkVFIUnweSDG0QG9Dedvn/J
48
+ tlHMRM0NXQSq6NxFPzjTyK4/J5g86V9LPEt6h+TpQACKxGxKa9cDdt6n8wDhskwV
49
+ V6xiwkq+j+tNh5+DIdKstceG8UulgdpgTt9r7kr9FVwL1L3ugJ4zK1fGYbTwPQRu
50
+ H9C0y7vZ/Mjm/osv9BAm0r57ywVgPU6af2yrTMhMkuCkMrIq4qW0XDiCTiso8QID
51
+ AQABMA0GCSqGSIb3DQEBBQUAA4IBAQCO6do2tYvE5/nLYnPXQbpUhhJLvZbJd+Nl
52
+ jLUlju2yzgNLFA3g19t9uKxKRG497//WdxHoa3lAPnY8IO+DgKp5dOMcKLopz+AX
53
+ jsElzCQxcPNqlYwGQgEDDm9q0WXs1Dg22y0Af1oR5Lx9XR1xWsw4z/6YEDUJRa7D
54
+ fAUIip+ocbMykuUvnrNU/wUDRv8VbsIoMr+1CK3pKACSUuB8ZhYfRUjgHEXKh4O5
55
+ I/hKy49jGW/9i5GOTEGGzOvHm2CIrpBPFWfKSJLjvLtYWbBuCyKK5+FsP4wuN8Rv
56
+ yLM1MDg00bd5M5MmTA1FFNGiGUqpfo7z+kcqRFOJqWm7bmaDvZVJ
57
+ -----END CERTIFICATE-----}
58
+
59
+ end
@@ -0,0 +1,53 @@
1
+ module Khipu
2
+ class HtmlHelper < KhipuService
3
+
4
+ def initialize(receiver_id, secret)
5
+ super(receiver_id, secret)
6
+ end
7
+
8
+ def create_payment_form(args, button = Khipu::FORM_BUTTONS['50x25'])
9
+ endpoint_url = Khipu::API_URL + 'createPaymentPage'
10
+ check_arguments(args, [:subject, :amount])
11
+ params = {
12
+ receiver_id: @receiver_id,
13
+ subject: args[:subject],
14
+ body: args[:body] || '',
15
+ amount: args[:amount]
16
+ }
17
+ payer_username = ''
18
+ if args[:payer_username]
19
+ payer_username = %Q{\n<input type="hidden" name="payer_username" value="#{args[:payer_username]}"/>}
20
+ params[:payer_username] = args[:payer_username]
21
+ end
22
+ params.merge!({
23
+ payer_email: args[:payer_email] || '',
24
+ bank_id: args[:bank_id] || '',
25
+ expires_date: args[:expires_date] || '',
26
+ transaction_id: args[:transaction_id] || '',
27
+ custom: args[:custom] || '',
28
+ notify_url: args[:notify_url] || '',
29
+ return_url: args[:return_url] || '',
30
+ cancel_url: args[:cancel_url] || '',
31
+ picture_url: args[:picture_url] || '',
32
+ })
33
+ params[:hash] = hmac_sha256(@secret, concatenated(params))
34
+ %Q{<form action="#{endpoint_url}" method="post">
35
+ <input type="hidden" name="receiver_id" value="#{params[:receiver_id]}">
36
+ <input type="hidden" name="subject" value="#{params[:subject]}"/>
37
+ <input type="hidden" name="body" value="#{params[:body]}">
38
+ <input type="hidden" name="amount" value="#{params[:amount]}">#{payer_username}
39
+ <input type="hidden" name="notify_url" value="#{params[:notify_url]}"/>
40
+ <input type="hidden" name="return_url" value="#{params[:return_url]}"/>
41
+ <input type="hidden" name="cancel_url" value="#{params[:cancel_url]}"/>
42
+ <input type="hidden" name="custom" value="#{params[:custom]}">
43
+ <input type="hidden" name="transaction_id" value="#{params[:transaction_id]}">
44
+ <input type="hidden" name="payer_email" value="#{params[:payer_email]}">
45
+ <input type="hidden" name="expires_date" value="#{params[:expires_date]}">
46
+ <input type="hidden" name="bank_id" value="#{params[:bank_id]}">
47
+ <input type="hidden" name="picture_url" value="#{params[:picture_url]}">
48
+ <input type="hidden" name="hash" value="#{params[:hash]}">
49
+ <input type="image" name="submit" src="#{button}">
50
+ </form>}
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,184 @@
1
+ require 'json'
2
+
3
+ module Khipu
4
+ class KhipuApiEndpoint < KhipuService
5
+
6
+ def initialize(receiver_id, secret)
7
+ super(receiver_id, secret)
8
+ end
9
+
10
+ def execute(endpoint, params, add_hash = true, json_response = true)
11
+ post_params = params.clone
12
+ if add_hash
13
+ post_params['hash'] = hmac_sha256(@secret, concatenated(params))
14
+ end
15
+ post(endpoint, post_params, json_response)
16
+ end
17
+
18
+ def payment_status(args)
19
+ endpoint = 'paymentStatus'
20
+ check_arguments(args, [:payment_id])
21
+ params = {
22
+ receiver_id: @receiver_id,
23
+ payment_id: args[:payment_id]
24
+ }
25
+ execute(endpoint, params)
26
+ end
27
+
28
+ def create_payment_url(args)
29
+ endpoint = 'createPaymentURL'
30
+ check_arguments(args, [:subject, :amount])
31
+ params = {
32
+ receiver_id: @receiver_id,
33
+ subject: args[:subject],
34
+ body: args[:body] || '',
35
+ amount: args[:amount],
36
+ payer_email: args[:payer_email] || '',
37
+ bank_id: args[:bank_id] || '',
38
+ expires_date: args[:expires_date] || '',
39
+ transaction_id: args[:transaction_id] || '',
40
+ custom: args[:custom] || '',
41
+ notify_url: args[:notify_url] || '',
42
+ return_url: args[:return_url] || '',
43
+ cancel_url: args[:cancel_url] || '',
44
+ picture_url: args[:picture_url] || ''
45
+ }
46
+ resp = execute(endpoint, params)
47
+ resp_items = %w(id url mobile-url)
48
+ resp.select{|key, value| resp_items.include?(key)}
49
+ end
50
+
51
+ def create_authenticated_payment_url(args)
52
+ endpoint = 'createAuthenticatedPaymentURL'
53
+ check_arguments(args, [:subject, :amount, :payer_username])
54
+ params = {
55
+ receiver_id: @receiver_id,
56
+ subject: args[:subject],
57
+ body: args[:body] || '',
58
+ amount: args[:amount],
59
+ payer_username: args[:payer_username],
60
+ payer_email: args[:payer_email] || '',
61
+ bank_id: args[:bank_id] || '',
62
+ expires_date: args[:expires_date] || '',
63
+ transaction_id: args[:transaction_id] || '',
64
+ custom: args[:custom] || '',
65
+ notify_url: args[:notify_url] || '',
66
+ return_url: args[:return_url] || '',
67
+ cancel_url: args[:cancel_url] || '',
68
+ picture_url: args[:picture_url] || ''
69
+ }
70
+ resp = execute(endpoint, params)
71
+ resp_items = %w(id url mobile-url)
72
+ resp.select{|key, value| resp_items.include?(key)}
73
+ end
74
+
75
+ def create_email(args)
76
+ endpoint = 'createEmail'
77
+ check_arguments(args, [:subject, :destinataries, :pay_directly, :send_emails])
78
+ params = {
79
+ receiver_id: @receiver_id,
80
+ subject: args[:subject],
81
+ body: args[:body] || '',
82
+ destinataries: args[:destinataries].to_json,
83
+ pay_directly: args[:pay_directly],
84
+ send_emails: args[:send_emails],
85
+ expires_date: args[:expires_date] || '',
86
+ transaction_id: args[:transaction_id] || '',
87
+ custom: args[:custom] || '',
88
+ notify_url: args[:notify_url] || '',
89
+ return_url: args[:return_url] || '',
90
+ cancel_url: args[:cancel_url] || '',
91
+ picture_url: args[:picture_url] || ''
92
+ }
93
+ execute(endpoint, params)
94
+ end
95
+
96
+ def receiver_banks
97
+ endpoint = 'receiverBanks'
98
+ params = {
99
+ receiver_id: @receiver_id
100
+ }
101
+ resp = execute(endpoint, params)
102
+ resp_items = %w(id name message min-amount)
103
+ resp['banks'].map{|item| item.select{|key, value| resp_items.include?(key)} }
104
+ end
105
+
106
+ def receiver_status
107
+ endpoint = 'receiverStatus'
108
+ params = {
109
+ receiver_id: @receiver_id,
110
+ }
111
+ execute(endpoint, params)
112
+ end
113
+
114
+ def set_bill_expired(args)
115
+ endpoint = 'setBillExpired'
116
+ check_arguments(args, [:bill_id])
117
+ params = {
118
+ receiver_id: @receiver_id,
119
+ bill_id: args[:bill_id],
120
+ text: args[:text] || ''
121
+ }
122
+ execute(endpoint, params)
123
+ end
124
+
125
+ def set_paid_by_receiver(args)
126
+ endpoint = 'setPaidByReceiver'
127
+ check_arguments(args, [:payment_id])
128
+ params = {
129
+ receiver_id: @receiver_id,
130
+ payment_id: args[:payment_id],
131
+ }
132
+ execute(endpoint, params)
133
+ end
134
+
135
+ def set_rejected_by_payer(args)
136
+ endpoint = 'setRejectedByPayer'
137
+ check_arguments(args, [:payment_id])
138
+ params = {
139
+ receiver_id: @receiver_id,
140
+ payment_id: args[:payment_id],
141
+ text: args[:text] || ''
142
+ }
143
+ execute(endpoint, params)
144
+ end
145
+
146
+ def verify_payment_notification(args)
147
+ endpoint = 'verifyPaymentNotification'
148
+ check_arguments(args, [:api_version, :notification_id, :subject, :amount, :payer_email, :notification_signature])
149
+ params = {
150
+ api_version: args[:api_version],
151
+ receiver_id: @receiver_id,
152
+ notification_id: args[:notification_id],
153
+ subject: args[:subject],
154
+ amount: args[:amount],
155
+ currency: args[:currency] || 'CLP',
156
+ transaction_id: args[:transaction_id] || '',
157
+ payer_email: args[:payer_email] || '',
158
+ custom: args[:custom] || '',
159
+ notification_signature: args[:notification_signature]
160
+ }
161
+ resp = execute(endpoint, params, false, false)
162
+ resp == 'VERIFIED'
163
+ end
164
+
165
+ def verify_payment_notification_local(args)
166
+ check_arguments(args, [:api_version, :notification_id, :subject, :amount, :payer_email, :notification_signature])
167
+ params = {
168
+ api_version: args[:api_version],
169
+ receiver_id: @receiver_id,
170
+ notification_id: args[:notification_id],
171
+ subject: args[:subject],
172
+ amount: args[:amount],
173
+ currency: args[:currency] || 'CLP',
174
+ transaction_id: args[:transaction_id] || '',
175
+ payer_email: args[:payer_email] || '',
176
+ custom: args[:custom] || '',
177
+ }
178
+ verify_signature(params, args[:notification_signature])
179
+ end
180
+
181
+
182
+
183
+ end
184
+ end
@@ -0,0 +1,76 @@
1
+ require 'net/http'
2
+ require 'openssl'
3
+ require 'base64'
4
+
5
+ module Khipu
6
+ class KhipuService
7
+
8
+ attr_accessor :receiver_id, :secret, :agent
9
+
10
+ def initialize(receiver_id, secret)
11
+ @receiver_id = receiver_id
12
+ @secret = secret
13
+ end
14
+
15
+ def post(endpoint, params, json_response = true)
16
+ uri = URI(Khipu::API_URL + endpoint)
17
+ http = Net::HTTP.new(uri.host, uri.port)
18
+ http.use_ssl = true
19
+ if Khipu::DEBUG
20
+ http.set_debug_output(Khipu::debug_stream)
21
+ end
22
+
23
+ params['agent'] = "lib-ruby-#{Khipu::VERSION} - #{@agent}" if @agent
24
+
25
+ req = Net::HTTP::Post.new(uri.request_uri, {'User-Agent' => "khipu-ruby-#{Khipu::VERSION}"})
26
+ req.set_form_data(params)
27
+ res = http.request(req)
28
+ if res.code == '400'
29
+ begin
30
+ error = JSON.parse(res.body)
31
+ raise ApiError.new(error['error']['type'], error['error']['message'])
32
+ rescue JSON::ParserError
33
+ raise "Invalid response from endpoint #{uri}"
34
+ end
35
+ elsif res.code != '200'
36
+ raise "Invalid response code #{res.code} endpoint #{uri}"
37
+ end
38
+ if Khipu::DEBUG
39
+ Khipu::debug_stream.puts res.body
40
+ end
41
+ if json_response
42
+ begin
43
+ JSON.parse(res.body)
44
+ rescue JSON::ParserError
45
+ raise "Invalid response from endpoint #{uri}"
46
+ end
47
+ else
48
+ res.body
49
+ end
50
+ end
51
+
52
+ def check_arguments(args, mandatory_args)
53
+ mandatory_args.each do |arg|
54
+ if (!args.has_key? arg)
55
+ raise(ArgumentError, "#{arg} not present")
56
+ end
57
+ end
58
+ end
59
+
60
+ def hmac_sha256(secret, data)
61
+ OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), secret, data).unpack('H*').first
62
+ end
63
+
64
+ def concatenated(params)
65
+ params.collect { |k, v| k.to_s + '=' + v.to_s }.join('&')
66
+ end
67
+
68
+ def verify_signature(params, signature)
69
+ cert_data = Khipu::dev_env? ? Khipu::CERT_DEV : Khipu::CERT_PROD
70
+ cert = OpenSSL::X509::Certificate.new(cert_data)
71
+ pkey = cert.public_key
72
+ pkey.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(signature), concatenated(params))
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ module Khipu
2
+ VERSION = "1.3.0"
3
+ end
data/lib/khipu.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'khipu/version'
2
+ require 'khipu/khipu_service'
3
+ require 'khipu/khipu_api_endpoint'
4
+ require 'khipu/html_helper'
5
+ require 'khipu/api_error'
6
+ require 'khipu/const'
7
+
8
+ module Khipu
9
+
10
+ API_URL = 'https://khipu.com/api/1.3/'
11
+ DEBUG = ENV['KHIPU_DEBUG'] || false
12
+ FORM_BUTTONS = {
13
+ '50x25' => '50x25.png',
14
+ '100x25' => '100x25.png',
15
+ '100x25white' => '100x25-w.png',
16
+ '100x50' => '100x50.png',
17
+ '100x50white' => '100x50-w.png',
18
+ '150x25' => '150x25.png',
19
+ '150x25white' => '150x25-w.png',
20
+ '150x50' => '150x50.png',
21
+ '150x50white' => '150x50-w.png',
22
+ '150x75' => '150x75.png',
23
+ '150x75white' => '150x75-w.png',
24
+ '200x50' => '200x50.png',
25
+ '200x50white' => '200x50-w.png',
26
+ '200x75' => '200x75.png',
27
+ '200x75white' => '200x75-w.png',
28
+ }.inject({}) { |h, (k, v)| h[k] = 'htps://s3.amazonaws.com/static.khipu.com/buttons/' + v; h }
29
+
30
+ def self.create_khipu_api(receiver_id, secret = '')
31
+ KhipuApiEndpoint.new(receiver_id, secret)
32
+ end
33
+
34
+ def self.create_html_helper(receiver_id, secret)
35
+ HtmlHelper.new(receiver_id, secret)
36
+ end
37
+
38
+ def self.debug_stream
39
+ $stdout
40
+ end
41
+
42
+ def self.dev_env?
43
+ ENV['KHIPU_ENV'] == 'DEV'
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ require "spec_helper"
2
+
3
+ describe 'KhipuService' do
4
+
5
+ it 'should calculate hmac' do
6
+ service = Khipu::KhipuService.new(1234, '1234')
7
+ hex_hmac = service.hmac_sha256('abcd', '1234')
8
+ expect(hex_hmac).to eq('d219a3dd877f943be0ee65ef6a34129778d2fd0568ce2d9a8f37fea9d7fcaca3')
9
+ end
10
+
11
+ it 'should concatenate parameters' do
12
+ service = Khipu::KhipuService.new(1234, '1234')
13
+ params = service.concatenated(a: 1, b: 'foo', c: nil)
14
+ expect(params).to eq('a=1&b=foo&c=')
15
+ end
16
+
17
+ end
@@ -0,0 +1 @@
1
+ require "khipu"
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: khipu
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Lorca
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: A wrapper for khipu's web API
56
+ email:
57
+ - alex.lorca@khipu.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - khipu.gemspec
68
+ - lib/khipu.rb
69
+ - lib/khipu/api_error.rb
70
+ - lib/khipu/const.rb
71
+ - lib/khipu/html_helper.rb
72
+ - lib/khipu/khipu_api_endpoint.rb
73
+ - lib/khipu/khipu_service.rb
74
+ - lib/khipu/version.rb
75
+ - spec/khipu_service_spec.rb
76
+ - spec/spec_helper.rb
77
+ homepage: https://github.com/khipu/lib-ruby
78
+ licenses:
79
+ - BSD
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '1.9'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.0.7
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: A wrapper for khipu's web API
101
+ test_files:
102
+ - spec/khipu_service_spec.rb
103
+ - spec/spec_helper.rb