darujme_cz 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # DarujmeCz
2
+ [![Build Status](https://travis-ci.org/luk4s/darujme_cz.svg?branch=master)](https://travis-ci.org/luk4s/darujme_cz)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/568759e4ec0a2e1960b2/maintainability)](https://codeclimate.com/github/luk4s/darujme_cz/maintainability)
4
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/568759e4ec0a2e1960b2/test_coverage)](https://codeclimate.com/github/luk4s/darujme_cz/test_coverage)
5
+
6
+ Darujme API https://www.darujme.cz/doc/api/v1/index.html.
7
+
8
+ This is a ruby library for access Darujme API. Get pledges for your organization, check transactions or donors.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'darujme_cz'
16
+ ```
17
+ ## Configuration
18
+
19
+ Firstly you need obtain `apiId` and `apiSecret` for your organization.
20
+
21
+ Then configure gem with `orgnazionation_id`, `api_id` and `api_secret`.
22
+
23
+ In your application create `config/initializers/darujme_cz.rb`
24
+
25
+ ```ruby
26
+ DarujmeCz.configure do |config|
27
+ config.organization_id = 434
28
+ config.app_id = "1234"
29
+ config.app_secret = "6b6fd1568d89c"
30
+ end
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```ruby
36
+ pledges = DarujmeCz::Pledge.where fromOutgoingDate: Date.yesterday
37
+ donor = pledges[0].donor # => DarujmeCz::Donor
38
+ donor.name # => "Jan Novák"
39
+ last_transaction = pledges[0].transactions.last # => DarujmeCz::Transaction
40
+ last_transaction.received_at # => 2019-05-06 00:00:00 +0200
41
+ last_transaction.outgoing_amount # => #<Money fractional:39200 currency:CZK>
42
+ ```
43
+
44
+ ## Development
45
+
46
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
47
+
48
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
49
+
50
+ ## Contributing
51
+
52
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/darujme_cz. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
53
+
54
+ ## License
55
+
56
+ GNU General Public License v3.0
57
+
58
+ See [COPYING](./COPYING) to see the full text.
59
+
60
+ ## Code of Conduct
61
+
62
+ Everyone interacting in the DarujmeCz project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/darujme_cz/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "darujme_cz"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require "pry"
11
+ Pry.start
12
+
13
+ # require "irb"
14
+ # IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,47 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "darujme_cz/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "darujme_cz"
7
+ spec.version = DarujmeCz::VERSION
8
+ spec.authors = ["Lukáš Pokorný"]
9
+ spec.email = ["luk4s.pokorny@gmail.com"]
10
+
11
+ spec.summary = 'Ruby library for work with Darujme.cz API'
12
+ spec.description = 'Get pledges or transactions from your organization on Darujme.cz'
13
+ spec.homepage = "https://github.com/luk4s/darujme_cz"
14
+ spec.license = "GPL-3.0-or-later"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
20
+
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "https://github.com/luk4s/darujme_cz"
23
+ spec.metadata["changelog_uri"] = "https://github.com/luk4s/darujme_cz/CHANGELOG.md"
24
+ else
25
+ raise "RubyGems 2.0 or newer is required to protect against " \
26
+ "public gem pushes."
27
+ end
28
+
29
+ # Specify which files should be added to the gem when it is released.
30
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
32
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
33
+ end
34
+ spec.test_files = Dir['spec/**/*']
35
+ # spec.bindir = "exe"
36
+ # spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
37
+ spec.require_paths = ["lib"]
38
+
39
+ spec.add_dependency 'activesupport', '> 5.2'
40
+ spec.add_dependency 'money', '~> 6.13'
41
+ spec.add_dependency 'rest-client', '~> 2.0'
42
+
43
+ spec.add_development_dependency "bundler", "~> 2.0"
44
+ spec.add_development_dependency "rake", "~> 10.0"
45
+ spec.add_development_dependency "rspec", "~> 3.0"
46
+ spec.add_development_dependency('webmock', '~> 3.4')
47
+ end
@@ -0,0 +1,20 @@
1
+ module DarujmeCz
2
+ class Address
3
+
4
+ # @param [Hash] attributes
5
+ def initialize(attributes)
6
+ @source = attributes
7
+ end
8
+
9
+ def to_s
10
+ "#{@source["street"]}\n#{@source["postCode"]} #{@source["city"]}\n#{@source["country"]}"
11
+ end
12
+
13
+ %w[street postCode city country].each do |m|
14
+ define_method m.underscore do
15
+ @source[m]
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ module DarujmeCz
2
+ class Base
3
+
4
+ # @abstract Define name of endpoint, which is also name of root element
5
+ def self.endpoint
6
+ raise NotImplementedError
7
+ end
8
+
9
+ # @param [Hash] params
10
+ # @option params [Integer] organization_id (nil) ID of organization
11
+ def self.all(**params)
12
+ where params
13
+ end
14
+
15
+ def self.where(**params)
16
+ c = Connection.new DarujmeCz.config.app_id, DarujmeCz.config.app_secret
17
+ org = params.delete(:organization_id) || DarujmeCz.config.organization_id
18
+ raise ArgumentError, "Missing organization ID" if org.nil?
19
+
20
+ data = c.get "organization/#{org}/#{endpoint}-by-filter", params
21
+ data[endpoint].map { |i| new(i) }
22
+ end
23
+
24
+ attr_reader :id
25
+
26
+ # @param [Hash] attributes
27
+ def initialize(attributes)
28
+ @source = attributes
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ require 'json'
2
+ require 'rest-client'
3
+ require 'uri'
4
+
5
+ module DarujmeCz
6
+ class Connection
7
+
8
+ URL = "https://www.darujme.cz/api/v1"
9
+
10
+ # @param [String] app_id
11
+ # @param [String] api_key
12
+ def initialize(app_id, api_key)
13
+ @app_id = app_id
14
+ @api_key = api_key
15
+ end
16
+
17
+ # @param [String] path
18
+ # @param [Hash] params
19
+ def get(path, **params)
20
+ begin
21
+ response = RestClient.get "#{URL}/#{path}", params: { apiId: @app_id, apiSecret: @api_key }.merge(params)
22
+ rescue RestClient::Exception => ex
23
+ handle_response_error(ex)
24
+ end
25
+ parse_response(response)
26
+ end
27
+
28
+ def post(path, **data)
29
+ # @todo
30
+ raise NotImplementedError
31
+ end
32
+
33
+ private
34
+
35
+ # @param [RestClient::Exception] exception
36
+ # @raise [RestClient::Exception]
37
+ def handle_response_error(exception)
38
+ data = parse_response(exception.response)
39
+ exception.message += "\n#{data["message"]}"
40
+ raise exception
41
+ end
42
+
43
+ def parse_response(response)
44
+ JSON.parse(response.body)
45
+ rescue JSON::ParserError => _ex
46
+ {}
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ module DarujmeCz
2
+ class Donor
3
+
4
+ delegate :city, :street, :postal_code, :country, to: :address
5
+ # @param [Hash] attributes
6
+ def initialize(attributes)
7
+ @source = attributes
8
+ end
9
+
10
+ def name
11
+ "#{first_name} #{last_name}"
12
+ end
13
+
14
+ def address
15
+ @address = Address.new(@source["address"]) if @source["address"]
16
+ end
17
+
18
+ %w[firstName lastName email phone].each do |m|
19
+ define_method m.underscore do
20
+ @source[m]
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ module DarujmeCz
2
+ # @see https://www.darujme.cz/doc/api/v1/index.html#endpoint-get-organization-organizationid-pledges-by-filter
3
+ class Pledge < Base
4
+
5
+ def self.endpoint
6
+ "pledges"
7
+ end
8
+
9
+ # @param [Hash] attributes
10
+ def initialize(attributes)
11
+ @id = attributes["pledgeId"]
12
+ super
13
+ end
14
+
15
+ def recurrent?
16
+ !!@source["isRecurrent"]
17
+ end
18
+
19
+ def want_donation_certificate?
20
+ !!@source["wantDonationCertificate"]
21
+ end
22
+
23
+ # @return [Money]
24
+ def amount
25
+ @amount ||= ::Money.new(*@source["pledgedAmount"].values)
26
+ end
27
+
28
+ # @return [Time]
29
+ def pledged_at
30
+ @source["pledgedAt"].to_time
31
+ end
32
+
33
+ %w[organizationId projectId promotionId paymentMethod].each do |m|
34
+ define_method m.underscore do
35
+ @source[m]
36
+ end
37
+ end
38
+
39
+ def donor
40
+ @donor ||= Donor.new @source["donor"]
41
+ end
42
+
43
+ def transactions
44
+ @transactions ||= Array(@source["transactions"]).collect do |transaction_source|
45
+ Transaction.new(transaction_source)
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,37 @@
1
+ module DarujmeCz
2
+ # @see https://www.darujme.cz/doc/api/v1/index.html#endpoint-get-organization-organizationid-transactions-by-filter
3
+ class Transaction < Base
4
+
5
+ def self.endpoint
6
+ "transactions"
7
+ end
8
+
9
+ # @param [Hash] attributes
10
+ def initialize(attributes)
11
+ @id = attributes["transactionId"]
12
+ super
13
+ end
14
+
15
+ def sent_amount
16
+ @sent_amount ||= ::Money.new(*@source["sentAmount"].values)
17
+ end
18
+
19
+ def outgoing_amount
20
+ @outgoing_amount ||= ::Money.new(*@source["outgoingAmount"].values)
21
+ end
22
+
23
+ def received_at
24
+ @source["receivedAt"].to_time
25
+ end
26
+
27
+ def pledge
28
+ @pledge ||= Pledge.new(@source["pledge"])
29
+ end
30
+
31
+ %w[outgoingVs outgoingBankAccount].each do |m|
32
+ define_method m.underscore do
33
+ @source[m]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module DarujmeCz
2
+ VERSION = "0.1.1"
3
+ end
data/lib/darujme_cz.rb ADDED
@@ -0,0 +1,19 @@
1
+ require "darujme_cz/version"
2
+ require "active_support/core_ext/string"
3
+ require "active_support/configurable"
4
+ require "money"
5
+ require "ostruct"
6
+
7
+ module DarujmeCz
8
+ class Error < StandardError; end
9
+ # Your code goes here...
10
+ autoload :Address, "darujme_cz/address"
11
+ autoload :Base, "darujme_cz/base"
12
+ autoload :Connection, "darujme_cz/connection"
13
+ autoload :Donor, "darujme_cz/donor"
14
+ autoload :Pledge, "darujme_cz/pledge"
15
+ autoload :Transaction, "darujme_cz/transaction"
16
+
17
+ include ActiveSupport::Configurable
18
+ config_accessor :app_id, :app_secret, :organization_id
19
+ end
@@ -0,0 +1,5 @@
1
+ RSpec.describe DarujmeCz do
2
+ it "has a version number" do
3
+ expect(DarujmeCz::VERSION).not_to be nil
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ {
2
+ "firstName": "Jan",
3
+ "lastName": "Novák",
4
+ "email": "jan.novak@seznam.cz",
5
+ "address": {
6
+ "street": "Otakarova 34",
7
+ "city": "Praha",
8
+ "postCode": "120 00",
9
+ "country": "ČR"
10
+ },
11
+ "phone": "+420 123 456 789"
12
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "pledges": [
3
+ {
4
+ "pledgeId": 1203450,
5
+ "organizationId": 2,
6
+ "projectId": 4563,
7
+ "promotionId": null,
8
+ "paymentMethod": "gp_webpay_charge",
9
+ "isRecurrent": true,
10
+ "pledgedAmount": {
11
+ "cents": 135800,
12
+ "currency": "CZK"
13
+ },
14
+ "pledgedAt": "2019-04-07T15:30:57+02:00",
15
+ "donor": {
16
+ "firstName": "Jan",
17
+ "lastName": "Novák",
18
+ "email": "jan.novak@seznam.cz",
19
+ "address": {
20
+ "street": "Otakarova 34",
21
+ "city": "Praha",
22
+ "postCode": "120 00",
23
+ "country": "ČR"
24
+ },
25
+ "phone": "+420 123 456 789"
26
+ },
27
+ "wantDonationCertificate": true,
28
+ "customFields": {},
29
+ "transactions": [
30
+ {
31
+ "transactionId": 1544,
32
+ "state": "success",
33
+ "sentAmount": {
34
+ "cents": 135800,
35
+ "currency": "CZK"
36
+ },
37
+ "receivedAt": "2019-04-08T00:00:00+02:00",
38
+ "outgoingAmount": {
39
+ "cents": 133084,
40
+ "currency": "CZK"
41
+ },
42
+ "outgoingVs": null,
43
+ "outgoingBankAccount": "01234/0100"
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "transactions": [
3
+ {
4
+ "transactionId": 3,
5
+ "state": "refund",
6
+ "sentAmount": {
7
+ "cents": 124500,
8
+ "currency": "CZK"
9
+ },
10
+ "receivedAt": "2019-04-07T15:30:57+02:00",
11
+ "outgoingAmount": {
12
+ "cents": 124500,
13
+ "currency": "CZK"
14
+ },
15
+ "outgoingVs": "6432997",
16
+ "outgoingBankAccount": "12345/0100",
17
+ "pledge": {
18
+ "pledgeId": 1203450,
19
+ "organizationId": 2,
20
+ "projectId": 4563,
21
+ "promotionId": null,
22
+ "paymentMethod": "gp_webpay_charge",
23
+ "isRecurrent": true,
24
+ "pledgedAmount": {
25
+ "cents": 678200,
26
+ "currency": "CZK"
27
+ },
28
+ "pledgedAt": "2019-04-07T15:30:57+02:00",
29
+ "donor": {
30
+ "firstName": "Jan",
31
+ "lastName": "Novák",
32
+ "email": "jan.novak@seznam.cz",
33
+ "address": {
34
+ "street": "Otakarova 34",
35
+ "city": "Praha",
36
+ "postCode": "120 00",
37
+ "country": "ČR"
38
+ },
39
+ "phone": "+420 123 456 789"
40
+ },
41
+ "wantDonationCertificate": true,
42
+ "customFields": {}
43
+ }
44
+ }
45
+ ]
46
+ }
@@ -0,0 +1,13 @@
1
+ RSpec.describe DarujmeCz::Connection do
2
+ subject { described_class.new "22", "8d948b1" }
3
+
4
+ describe ".get" do
5
+ it "get all" do
6
+ stub_request(:get, "https://www.darujme.cz/api/v1/organization/22/pledges-by-filter?apiId=22&apiSecret=8d948b1").
7
+ to_return(status: 200, body: file_fixture("pledges.json"))
8
+ data = subject.get "organization/22/pledges-by-filter"
9
+ expect(data).to include "pledges"
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,29 @@
1
+ RSpec.describe DarujmeCz::Donor do
2
+ let(:attributes) { JSON.parse(file_fixture("donor.json").read) }
3
+
4
+ subject { described_class.new attributes }
5
+
6
+ it "#attributes" do
7
+ expect(subject).to have_attributes first_name: "Jan", last_name: "Novák"
8
+ end
9
+
10
+ it "#name" do
11
+ expect(subject.name).to eq "Jan Novák"
12
+ end
13
+
14
+ describe "#address" do
15
+ it { expect(subject.address).to be_a DarujmeCz::Address }
16
+ it "text address" do
17
+ expect(subject.address.to_s).to eq "Otakarova 34\n120 00 Praha\nČR"
18
+ end
19
+
20
+ it "#city" do
21
+ expect(subject.address.city).to eq "Praha"
22
+ end
23
+ end
24
+
25
+ it "#street" do
26
+ expect(subject.street).to eq "Otakarova 34"
27
+ end
28
+
29
+ end
@@ -0,0 +1,85 @@
1
+ require "rest-client"
2
+ RSpec.describe DarujmeCz::Pledge do
3
+ describe ".all" do
4
+ it "get all" do
5
+ stub_request(:get, "https://www.darujme.cz/api/v1/organization/#{DarujmeCz.config.organization_id}/pledges-by-filter?apiId=#{DarujmeCz.config.app_id}&apiSecret=#{DarujmeCz.config.app_secret}").
6
+ to_return(status: 200, body: file_fixture("pledges.json"))
7
+
8
+ pledges = described_class.all
9
+ expect(pledges).to be_a Array
10
+ expect(pledges[0]).to have_attributes "organization_id" => 2
11
+ end
12
+
13
+ it "without organization_id" do
14
+ DarujmeCz.config.organization_id = nil
15
+ expect { described_class.all }.to raise_exception ArgumentError
16
+ end
17
+
18
+ it "without credentials" do
19
+ DarujmeCz.config.app_id = nil
20
+
21
+ stub_request(:get, "https://www.darujme.cz/api/v1/organization/#{DarujmeCz.config.organization_id}/pledges-by-filter?apiId&apiSecret=#{DarujmeCz.config.app_secret}").
22
+ to_return(status: 400, body: darujme_cz_response_error(message: "Missing required parameter").to_json)
23
+
24
+ expect { described_class.all }.to raise_exception RestClient::BadRequest, "400 Bad Request\nMissing required parameter"
25
+ end
26
+
27
+ it "pass custom organization_id" do
28
+ stub = stub_request(:get, "https://www.darujme.cz/api/v1/organization/33/pledges-by-filter?apiId=#{DarujmeCz.config.app_id}&apiSecret=#{DarujmeCz.config.app_secret}").
29
+ to_return(status: 200, body: file_fixture("pledges.json"))
30
+
31
+ described_class.all(organization_id: 33)
32
+ expect(stub).to have_been_made
33
+ end
34
+ end
35
+
36
+ describe ".where" do
37
+ it "fromOutgoingDate" do
38
+ stub = stub_request(:get, "https://www.darujme.cz/api/v1/organization/#{DarujmeCz.config.organization_id}/pledges-by-filter?apiId=#{DarujmeCz.config.app_id}&apiSecret=#{DarujmeCz.config.app_secret}&fromOutgoingDate=#{Date.today.strftime("%Y-%m-%d")}").
39
+ to_return(status: 200, body: file_fixture("pledges.json"))
40
+ described_class.where fromOutgoingDate: Date.today
41
+
42
+ expect(stub).to have_been_made
43
+ end
44
+ end
45
+
46
+ let(:json) { JSON.parse file_fixture("pledges.json").read }
47
+ subject { described_class.new json["pledges"][0] }
48
+
49
+ describe "attributes" do
50
+
51
+ it "inspect" do
52
+ expect(subject.project_id).to eq 4563
53
+ expect(subject.id).to eq 1203450
54
+ end
55
+ end
56
+
57
+ describe "#amount" do
58
+ it { expect(subject.amount.to_f).to eq 1358.0 }
59
+ it { expect(subject.amount.currency).to eq "CZK" }
60
+ end
61
+
62
+ describe "#pledged_at" do
63
+ it { expect(subject.pledged_at).to be_a Time }
64
+ end
65
+
66
+ describe "#recurrent?" do
67
+ it { expect(subject.recurrent?).to eq true }
68
+ end
69
+
70
+ describe "#want_donation_certificate?" do
71
+ it { expect(subject.want_donation_certificate?).to eq true }
72
+ end
73
+
74
+ describe "#donor" do
75
+ it { expect(subject.donor).to be_a DarujmeCz::Donor }
76
+ end
77
+
78
+ describe "#transactions" do
79
+ it "be a array" do
80
+ expect(subject.transactions).to be_a Array
81
+ expect(subject.transactions[0]).to be_a DarujmeCz::Transaction
82
+ end
83
+ end
84
+
85
+ end
@@ -0,0 +1,32 @@
1
+ RSpec.describe DarujmeCz::Transaction do
2
+ describe ".all" do
3
+ it "get all" do
4
+ stub_request(:get, "https://www.darujme.cz/api/v1/organization/#{DarujmeCz.config.organization_id}/transactions-by-filter?apiId=#{DarujmeCz.config.app_id}&apiSecret=#{DarujmeCz.config.app_secret}").
5
+ to_return(status: 200, body: file_fixture("transactions.json"))
6
+
7
+ pledges = described_class.all
8
+ expect(pledges).to be_a Array
9
+ expect(pledges[0]).to have_attributes "outgoing_bank_account" => "12345/0100"
10
+ end
11
+ end
12
+
13
+ let(:json) { JSON.parse file_fixture("transactions.json").read }
14
+ subject { described_class.new json["transactions"][0] }
15
+
16
+ it "#pledge" do
17
+ expect(subject.pledge).to be_a DarujmeCz::Pledge
18
+ end
19
+
20
+ it "#sent_amount" do
21
+ expect(subject.sent_amount).to be_a Money
22
+ end
23
+
24
+ it "#outgoing_amount" do
25
+ expect(subject.outgoing_amount).to be_a Money
26
+ end
27
+
28
+ it "#received_at" do
29
+ expect(subject.received_at).to be_a Time
30
+ end
31
+
32
+ end