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 +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +29 -0
- data/README.md +229 -0
- data/Rakefile +7 -0
- data/khipu.gemspec +26 -0
- data/lib/khipu/api_error.rb +10 -0
- data/lib/khipu/const.rb +59 -0
- data/lib/khipu/html_helper.rb +53 -0
- data/lib/khipu/khipu_api_endpoint.rb +184 -0
- data/lib/khipu/khipu_service.rb +76 -0
- data/lib/khipu/version.rb +3 -0
- data/lib/khipu.rb +45 -0
- data/spec/khipu_service_spec.rb +17 -0
- data/spec/spec_helper.rb +1 -0
- metadata +103 -0
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
data/Gemfile
ADDED
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
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
|
data/lib/khipu/const.rb
ADDED
@@ -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
|
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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|