mintchip 0.0.2 → 0.0.3

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.
@@ -1,141 +1,20 @@
1
- require 'openssl'
2
- require 'net/http'
3
- require 'net/https'
4
- require 'uri'
5
- require 'json'
6
- require 'active_support/all'
7
-
8
- require "mintchip/version"
1
+ require 'mintchip/version'
2
+ require 'mintchip/hosted'
3
+ require 'mintchip/mintchip_message'
4
+ require 'mintchip/value_request_message'
9
5
 
10
6
  module Mintchip
11
7
  CURRENCY_CODES = { CAD: 1 }
12
8
 
9
+ def self.currency_code(name)
10
+ CURRENCY_CODES[name.to_sym] or raise InvalidCurrency
11
+ end
12
+
13
13
  class InvalidCurrency < StandardError; end
14
14
  class Error < StandardError ; end
15
15
  class SystemError < Error ; end
16
16
  class CryptoError < Error ; end
17
17
  class FormatError < Error ; end
18
18
  class MintchipError < Error ; end
19
-
20
-
21
- def self.currency_code(name)
22
- CURRENCY_CODES[name.to_sym] or raise InvalidCurrency
23
- end
24
-
25
- class Hosted
26
- RESOURCE_BASE = "https://remote.mintchipchallenge.com/mintchip"
27
-
28
- def initialize(key, password)
29
- @p12 = OpenSSL::PKCS12.new(key, password)
30
- end
31
-
32
- # GET /info/{responseformat}
33
- def info
34
- @info ||= Info.new get "/info/json"
35
- end
36
-
37
- class Info
38
- ATTRIBUTES = %w(id currencyCode balance creditLogCount debitLogCount) +
39
- %w(creditLogCountRemaining debitLogCountRemaining maxCreditAllowed maxDebitAllowed version)
40
-
41
- attr_reader *ATTRIBUTES.map(&:underscore).map(&:to_sym)
42
-
43
- def initialize(str)
44
- attrs = JSON.parse(str)
45
- ATTRIBUTES.each do |attr|
46
- instance_variable_set("@#{attr.underscore}", attrs[attr])
47
- end
48
- end
49
- end
50
-
51
- # POST /receipts
52
- def load_value(vm)
53
- res = post "/receipts", vm, "application/vnd.scg.ecn-message"
54
- # body is an empty string on success, which is gross.
55
- res == ""
56
- end
57
-
58
- # POST /payments/request
59
- def create_value1(vrm)
60
- post "/payments/request", vrm, "application/vnd.scg.ecn-request"
61
- end
62
-
63
- def create_value2(payee_id, currency_name, amount_in_cents)
64
- currency_code = Mintchip.currency_code(currency_name)
65
- post "/payments/#{payee_id}/#{currency_code}/#{amount_in_cents}"
66
- end
67
-
68
- # GET /payments/lastdebit
69
- def last_debit
70
- get "/payments/lastdebit"
71
- end
72
-
73
- # GET /payments/{startindex}/{stopindex}/{responseformat}
74
- def debit_log(start, stop)
75
- list = JSON.parse get "/payments/#{start}/#{stop}/json"
76
- list.map{|item| TransactionLogEntry.new item}
77
- end
78
-
79
- # GET /receipts/{startindex}/{stopindex}/{responseformat}
80
- def credit_log(start, stop)
81
- list = JSON.parse get "/receipts/#{start}/#{stop}/json"
82
- list.map{|item| TransactionLogEntry.new item}
83
- end
84
-
85
- class TransactionLogEntry
86
- ATTRIBUTES = %w(amount challenge index logType payerId payeeId transactionTime currencyCode)
87
-
88
- attr_reader *ATTRIBUTES.map(&:underscore).map(&:to_sym)
89
-
90
- def initialize(attrs)
91
- ATTRIBUTES.each do |attr|
92
- instance_variable_set("@#{attr.underscore}", attrs[attr])
93
- end
94
- end
95
- end
96
-
97
- private
98
-
99
- def connection
100
- uri = URI.parse RESOURCE_BASE
101
- https = Net::HTTP.new(uri.host, uri.port)
102
- https.use_ssl = true
103
- https.cert = @p12.certificate
104
- https.key = @p12.key
105
- https.ca_file = File.expand_path("../../mintchip.pem", __FILE__)
106
- https.verify_mode = OpenSSL::SSL::VERIFY_PEER
107
-
108
- https
109
- end
110
-
111
- def get(path)
112
- uri = URI.parse(RESOURCE_BASE + path)
113
- req = Net::HTTP::Get.new(uri.path)
114
- handle_response connection.start { |cx| cx.request(req) }
115
- end
116
-
117
- def post(path, data = {}, content_type = nil)
118
- uri = URI.parse(RESOURCE_BASE + path)
119
- req = Net::HTTP::Post.new(uri.path)
120
-
121
- Hash === data ? req.set_form_data(data) : req.body = data
122
- req.content_type = content_type if content_type
123
-
124
- handle_response connection.start { |cx| cx.request(req) }
125
- end
126
-
127
- def handle_response(resp)
128
- case resp.code.to_i
129
- when 200 ; resp.body
130
- when 452 ; raise Mintchip::SystemError, resp.msg
131
- when 453 ; raise Mintchip::CryptoError, resp.msg
132
- when 454 ; raise Mintchip::FormatError, resp.msg
133
- when 455 ; raise Mintchip::MintchipError, resp.msg
134
- else ; raise Mintchip::Error, resp.msg
135
- end
136
- end
137
-
138
- end
139
-
140
19
  end
141
20
 
@@ -0,0 +1,100 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'openssl'
4
+ require 'uri'
5
+
6
+ require 'active_support/all'
7
+ require 'json'
8
+
9
+ require 'mintchip/hosted/info'
10
+ require 'mintchip/hosted/transaction_log_entry'
11
+
12
+ module Mintchip
13
+ class Hosted
14
+ RESOURCE_BASE = "https://remote.mintchipchallenge.com/mintchip"
15
+
16
+ def initialize(key, password)
17
+ @p12 = OpenSSL::PKCS12.new(key, password)
18
+ end
19
+
20
+ # GET /info/{responseformat}
21
+ def info
22
+ @info ||= Info.new get "/info/json"
23
+ end
24
+
25
+ # POST /receipts
26
+ def load_value(vm)
27
+ res = post "/receipts", vm, "application/vnd.scg.ecn-message"
28
+ # body is an empty string on success, which is gross.
29
+ res == ""
30
+ end
31
+
32
+ # POST /payments/request
33
+ def create_value1(vrm)
34
+ post "/payments/request", vrm, "application/vnd.scg.ecn-request"
35
+ end
36
+
37
+ def create_value2(payee_id, currency_name, amount_in_cents)
38
+ currency_code = Mintchip.currency_code(currency_name)
39
+ post "/payments/#{payee_id}/#{currency_code}/#{amount_in_cents}"
40
+ end
41
+
42
+ # GET /payments/lastdebit
43
+ def last_debit
44
+ get "/payments/lastdebit"
45
+ end
46
+
47
+ # GET /payments/{startindex}/{stopindex}/{responseformat}
48
+ def debit_log(start, stop)
49
+ list = JSON.parse get "/payments/#{start}/#{stop}/json"
50
+ list.map{|item| TransactionLogEntry.new item}
51
+ end
52
+
53
+ # GET /receipts/{startindex}/{stopindex}/{responseformat}
54
+ def credit_log(start, stop)
55
+ list = JSON.parse get "/receipts/#{start}/#{stop}/json"
56
+ list.map{|item| TransactionLogEntry.new item}
57
+ end
58
+
59
+ private
60
+
61
+ def connection
62
+ uri = URI.parse RESOURCE_BASE
63
+ https = Net::HTTP.new(uri.host, uri.port)
64
+ https.use_ssl = true
65
+ https.cert = @p12.certificate
66
+ https.key = @p12.key
67
+ https.ca_file = File.expand_path("../../../mintchip.pem", __FILE__)
68
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
69
+
70
+ https
71
+ end
72
+
73
+ def get(path)
74
+ uri = URI.parse(RESOURCE_BASE + path)
75
+ req = Net::HTTP::Get.new(uri.path)
76
+ handle_response connection.start { |cx| cx.request(req) }
77
+ end
78
+
79
+ def post(path, data = {}, content_type = nil)
80
+ uri = URI.parse(RESOURCE_BASE + path)
81
+ req = Net::HTTP::Post.new(uri.path)
82
+
83
+ Hash === data ? req.set_form_data(data) : req.body = data
84
+ req.content_type = content_type if content_type
85
+
86
+ handle_response connection.start { |cx| cx.request(req) }
87
+ end
88
+
89
+ def handle_response(resp)
90
+ case resp.code.to_i
91
+ when 200 ; resp.body
92
+ when 452 ; raise Mintchip::SystemError, resp.msg
93
+ when 453 ; raise Mintchip::CryptoError, resp.msg
94
+ when 454 ; raise Mintchip::FormatError, resp.msg
95
+ when 455 ; raise Mintchip::MintchipError, resp.msg
96
+ else ; raise Mintchip::Error, resp.msg
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,17 @@
1
+ module Mintchip
2
+ class Hosted
3
+ class Info
4
+ ATTRIBUTES = %w(id currencyCode balance creditLogCount debitLogCount) +
5
+ %w(creditLogCountRemaining debitLogCountRemaining maxCreditAllowed maxDebitAllowed version)
6
+
7
+ attr_reader *ATTRIBUTES.map(&:underscore).map(&:to_sym)
8
+
9
+ def initialize(str)
10
+ attrs = JSON.parse(str)
11
+ ATTRIBUTES.each do |attr|
12
+ instance_variable_set("@#{attr.underscore}", attrs[attr])
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module Mintchip
2
+ class Hosted
3
+ class TransactionLogEntry
4
+ ATTRIBUTES = %w(amount challenge index logType payerId payeeId transactionTime currencyCode)
5
+
6
+ attr_reader *ATTRIBUTES.map(&:underscore).map(&:to_sym)
7
+
8
+ def initialize(attrs)
9
+ ATTRIBUTES.each do |attr|
10
+ instance_variable_set("@#{attr.underscore}", attrs[attr])
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ module Mintchip
2
+ class MintchipMessage
3
+
4
+ def initialize(value, response_url, annotation, *)
5
+ @value = value
6
+ @response_url = response_url
7
+ @annotation = annotation
8
+ end
9
+
10
+ def to_base64
11
+ Base64.strict_encode64(message.to_der)
12
+ end
13
+
14
+ protected
15
+
16
+ def packet
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def message
21
+ message_version = OpenSSL::ASN1::Enumerated.new(1, 0, :EXPLICIT)
22
+ annotation = OpenSSL::ASN1::IA5String.new(annotation, 1, :EXPLICIT)
23
+ message = OpenSSL::ASN1::Sequence.new([message_version, @annotation, packet], 0, :EXPLICIT, :APPLICATION)
24
+ end
25
+
26
+ def to_binary_coded_decimal(n)
27
+ str = n.to_s
28
+ bin = ""
29
+ str.each_char do |c|
30
+ bin << c.to_i.to_s(2).rjust(4,'0')
31
+ end
32
+ bin
33
+ end
34
+
35
+ def to_padded_ascii_binary_coded_decimal(value, length_in_bits, pad_character = '0')
36
+ res = to_binary_coded_decimal(value).to_s
37
+ res = res.rjust(length_in_bits, pad_character)
38
+ res = [res].pack("B*")
39
+ end
40
+
41
+ def to_octet_string(value, tag = -1, tagging = :EXPLICIT)
42
+ ret = tag == -1 ? OpenSSL::ASN1::OctetString.new(value) : OpenSSL::ASN1::OctetString.new(value, tag, tagging)
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,55 @@
1
+ module Mintchip
2
+ class ValueRequestMessage < MintchipMessage
3
+
4
+ def initialize(value, response_url, annotation, info)
5
+ super
6
+ @info = info
7
+ end
8
+
9
+ def packet
10
+ OpenSSL::ASN1::ASN1Data.new([sequence], 2, :CONTEXT_SPECIFIC)
11
+ end
12
+
13
+ private
14
+
15
+ def sequence
16
+ OpenSSL::ASN1::Sequence.new(sequence_data, 1, :EXPLICIT)
17
+ end
18
+
19
+ def sequence_data
20
+ [payee_id, currency_code, transfer_value, include_cert,
21
+ response_url, random_challenge]
22
+ end
23
+
24
+ def payee_id
25
+ to_octet_string(to_padded_ascii_binary_coded_decimal(@info.id, 64))
26
+ end
27
+
28
+ def currency_code
29
+ to_octet_string(@info.currency_code.chr)
30
+ end
31
+
32
+ def include_cert
33
+ OpenSSL::ASN1::Boolean.new(true)
34
+ end
35
+
36
+ def transfer_value
37
+ to_octet_string(to_padded_ascii_binary_coded_decimal(@value, 24))
38
+ end
39
+
40
+ def response_url
41
+ OpenSSL::ASN1::IA5String(@response_url)
42
+ end
43
+
44
+ def random_challenge
45
+ to_octet_string(random_bytes(4), 0, :IMPLICIT)
46
+ end
47
+
48
+ def random_bytes(n)
49
+ Random.new().bytes(n)
50
+ end
51
+
52
+ end
53
+ end
54
+
55
+
@@ -1,3 +1,3 @@
1
1
  module Mintchip
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -14,4 +14,10 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "mintchip"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Mintchip::VERSION
17
+
18
+ gem.add_dependency 'active_support'
19
+ gem.add_dependency 'json'
20
+
21
+ gem.add_development_dependency 'minitest'
22
+ gem.add_development_dependency 'mocha'
17
23
  end
@@ -0,0 +1,22 @@
1
+ class Mintchip::HostedTest < MiniTest::Unit::TestCase
2
+
3
+ # Testing web APIs is a pain in the ass. TODO!
4
+ def test_info
5
+ assert_equal 3, hosted.info
6
+ end
7
+
8
+ def setup
9
+ OpenSSL::PKCS12.expects(:new).with("omg", "wtf").returns(p12)
10
+ end
11
+
12
+ private
13
+
14
+ def p12
15
+ @p12 ||= stub(key: "key", certificate: "certificate")
16
+ end
17
+
18
+ def hosted
19
+ @hosted ||= Mintchip::Hosted.new("omg", "wtf")
20
+ end
21
+
22
+ end
@@ -0,0 +1,18 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class ValueRequestMessageTest < MiniTest::Unit::TestCase
4
+
5
+ def test_pretty_much_everything
6
+ msg = Mintchip::ValueRequestMessage.new(1000, "http://example.com", "", info)
7
+ msg.expects(:random_bytes).with(4).returns(">@\xE6\x90")
8
+ expected = "YDwwOqADCgEBojOhMTAvBAgQAAAAAAATJAQBAQQDABAAAQH/FhJodHRwOi8vZXhhbXBsZS5jb22ABD5A5pA="
9
+ assert_equal expected, msg.to_base64
10
+ end
11
+
12
+ private
13
+
14
+ def info
15
+ stub(id: "1000000000001324", currency_code: 1)
16
+ end
17
+
18
+ end
@@ -0,0 +1,7 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'mintchip'
4
+
5
+ require 'minitest/autorun'
6
+ require 'mocha'
7
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mintchip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,52 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-11 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2012-04-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: active_support
16
+ requirement: &70097927569880 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70097927569880
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ requirement: &70097927618500 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70097927618500
36
+ - !ruby/object:Gem::Dependency
37
+ name: minitest
38
+ requirement: &70097927617540 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70097927617540
47
+ - !ruby/object:Gem::Dependency
48
+ name: mocha
49
+ requirement: &70097927616860 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70097927616860
14
58
  description: ! 'Pre-Alpha: API for RCM MintChip'
15
59
  email:
16
60
  - burke@burkelibbey.org
@@ -24,9 +68,17 @@ files:
24
68
  - README.md
25
69
  - Rakefile
26
70
  - lib/mintchip.rb
71
+ - lib/mintchip/hosted.rb
72
+ - lib/mintchip/hosted/info.rb
73
+ - lib/mintchip/hosted/transaction_log_entry.rb
74
+ - lib/mintchip/mintchip_message.rb
75
+ - lib/mintchip/value_request_message.rb
27
76
  - lib/mintchip/version.rb
28
77
  - mintchip.gemspec
29
78
  - mintchip.pem
79
+ - test/mintchip/hosted_test.rb
80
+ - test/mintchip/value_request_message_test.rb
81
+ - test/test_helper.rb
30
82
  homepage: ''
31
83
  licenses: []
32
84
  post_install_message:
@@ -51,4 +103,7 @@ rubygems_version: 1.8.11
51
103
  signing_key:
52
104
  specification_version: 3
53
105
  summary: ! 'Pre-Alpha: API for RCM MintChip.'
54
- test_files: []
106
+ test_files:
107
+ - test/mintchip/hosted_test.rb
108
+ - test/mintchip/value_request_message_test.rb
109
+ - test/test_helper.rb