nfg-client 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZDhhZGYxODA1OTNjMTcwNWJiMDVjMzUwOTE5NTgzNzEwOGQ3NDY5Mg==
5
+ data.tar.gz: !binary |-
6
+ ZmVjOTFiMWM5ZTA2NGQwMzc5NjNmYWZjNTBlYjllYjlkNDllNmNlMw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ OTU5ZjhlZTQ1NGNlNDNiNDY0MDI1OTI2ODA1N2JhZDk5NGJlNzgxYWQ0Njgy
10
+ YzRlZGRlOWY0MjA0MmMyYTY4MGE1NDg4ZTk3NmJmNjQ5ODA5MTg3M2U4OThl
11
+ ODYyODRlNGNjNjQ1ZGI3MjJkZTc0ZGEwNmExZjhiMzBhMzA4YmE=
12
+ data.tar.gz: !binary |-
13
+ ODc3MTViZmU1YzBmNzI2NjMwMGVhNWQ5OGRkNzg0MTliNzcyYjc2N2M1NzM2
14
+ ZTczOTMzYWE1YzQyNTgyNzA0MGE4ODMyMGFiNzc1ZjMxNGNhYmJmZTE1OGVm
15
+ NTM3NWY3MzAxMTFmZjNjOTA2ZDY2NGJjMDQ0Yjg4ZTk4ZWU2Mzc=
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ coverage
6
+ /.project
7
+ /nbproject/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in nfg-client.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Antonio Ruano Cuesta
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,360 @@
1
+ # NFG Client
2
+
3
+ A client to interact with the NFG (Network for Good) API via SOAP requests.
4
+
5
+ ## Installation
6
+
7
+ Install the gem:
8
+
9
+ gem install nfg-client
10
+
11
+ Or add it to your Gemfile:
12
+
13
+ gem "nfg-client"
14
+
15
+ ## Usage
16
+
17
+ Create a NFG Client instance using your NFG credentials:
18
+
19
+ nfg_client = NFGClient.new(partner_id, partner_password, partner_source, partner_campaign, use_sandbox)
20
+
21
+ The fifth parameter is optional (defaults to false), and indicates the Client which API URLs to use (sandbox or production).
22
+
23
+ Use the Client to make calls to the NFG API, each call will take a hash with the parameters you want to send along with it (apart from the NFG credentials).
24
+
25
+ Example:
26
+
27
+ nfg_client.get_donor_cofs({"DonorToken" => donor_token})
28
+
29
+ All required parameters for the different calls should have the name specified by the NFG documentation.
30
+
31
+ For more information, refer to the official NFG documentation:
32
+
33
+ http://www.thenetworkforgood.org/t5/Developer-Resource-Center/ct-p/Developer
34
+
35
+ ## Supported Calls
36
+
37
+ Below, the list of supported calls and examples of usage. All functions return a hash with the response from NFG.
38
+
39
+ ### CreateCOF
40
+
41
+ Creates a Card on File without a transaction.
42
+
43
+ **_Call_**
44
+
45
+ response = nfg_client.create_cof({
46
+ "DonorToken" => "",
47
+ "DonorFirstName" => "",
48
+ "DonorLastName" => "",
49
+ "DonorEmail" => "",
50
+ "DonorAddress1" => "",
51
+ "DonorAddress2" => "",
52
+ "DonorCity" => "",
53
+ "DonorState" => "",
54
+ "DonorZip" => "",
55
+ "DonorCountry" => "",
56
+ "DonorPhone" => "",
57
+ "CardType" => "", # Unk, Visa, Mastercard, Amex or Discover
58
+ "NameOnCard" => "",
59
+ "CardNumber" => "",
60
+ "ExpMonth" => "",
61
+ "ExpYear" => "",
62
+ "CSC" => ""
63
+ })
64
+
65
+ **_Response on Success_**
66
+
67
+ {
68
+ "StatusCode" => "Success",
69
+ "Message" => "", # Empty
70
+ "ErrorDetails" => "", # Empty
71
+ "CallDuration" => "x.x",
72
+ "DonorToken" => "x",
73
+ "COFId" => "x" # Integer
74
+ }
75
+
76
+ ### GetDonorCOFs
77
+
78
+ Gets a list of donor Cards on File.
79
+
80
+ **_Call_**
81
+
82
+ response = nfg_client.get_donor_cofs({
83
+ "DonorToken" => ""
84
+ })
85
+
86
+ **_Response on Success_**
87
+
88
+ {
89
+ "StatusCode" => "Success",
90
+ "Message" => "", # Empty
91
+ "ErrorDetails" => "", # Empty
92
+ "CallDuration" => "x.x",
93
+ "DonorToken" => "x",
94
+ "Cards" => [
95
+ {
96
+ "COFId" => "x", # Integer
97
+ "CardType" => "x", # e.g. Mastercard
98
+ "CCSuffix" => "xxxx", # Last four digits
99
+ "CCExpMonth" => "", # 1 or 2 digits
100
+ "CCExpYear" => "", # 4 digits
101
+ "bInUseByLiveRD" => "", # true or false
102
+ "COFEmailAddress" => ""
103
+ },
104
+ ...
105
+ ]
106
+ }
107
+
108
+ ### DeleteDonorCOF
109
+
110
+ Removes an existing Card on File.
111
+
112
+ **_Call_**
113
+
114
+ response = nfg_client.delete_donor_cof({
115
+ "DonorToken" => "",
116
+ "COFId" => ""
117
+ })
118
+
119
+ **_Response on Success_**
120
+
121
+ {
122
+ "StatusCode" => "Success",
123
+ "Message" => "", # Empty
124
+ "ErrorDetails" => "", # Empty
125
+ "CallDuration" => "x.xx"
126
+ }
127
+
128
+ ### MakeCOFDonation
129
+
130
+ Triggers a one-time or recurring donation to one or more charities with an existing Card on File.
131
+
132
+ **_Call_**
133
+
134
+ response = nfg_client.make_cof_donation({
135
+ "DonationLineItems" => {
136
+ "DonationItem1" => {
137
+ "NpoEin" => "",
138
+ "donorVis" => "", # ProvideAll, ProvideNameAndEmailOnly or Anonymous
139
+ "ItemAmount" => "",
140
+ "RecurType" => "", # NotRecurring, Monthly, Quarterly or Annually
141
+ "Designation" => ""
142
+ "Dedication" => ""
143
+ },
144
+ "DonationItem2" => {
145
+ ...
146
+ },
147
+ ...
148
+ },
149
+ "TotalAmount" => "",
150
+ "TipAmount" => "",
151
+ "DonorIpAddress" => "",
152
+ "DonorToken" => "",
153
+ "COFId" => ""
154
+ })
155
+
156
+ **_Response on Success_**
157
+
158
+ {
159
+ "StatusCode" => "Success",
160
+ "Message" => "", # Empty
161
+ "ErrorDetails" => "", # Empty
162
+ "CallDuration" => "x.x",
163
+ "ChargeId" => "x", # Integer
164
+ "COFId" => "0"
165
+ }
166
+
167
+ ### MakeDonationAddCOF
168
+
169
+ Triggers a one-time or recurring donation to one or more charities with a new Card on File.
170
+
171
+ **_Call_**
172
+
173
+ response = nfg_client.make_donation_add_cof({
174
+ "DonationLineItems" => {
175
+ "DonationItem1" => {
176
+ "NpoEin" => "",
177
+ "donorVis" => "", # ProvideAll, ProvideNameAndEmailOnly or Anonymous
178
+ "ItemAmount" => "",
179
+ "RecurType" => "", # NotRecurring, Monthly, Quarterly or Annually
180
+ "Designation" => ""
181
+ "Dedication" => ""
182
+ },
183
+ "DonationItem2" => {
184
+ ...
185
+ },
186
+ ...
187
+ },
188
+ "TotalAmount" => "",
189
+ "TipAmount" => "",
190
+ "DonorIpAddress" => "",
191
+ "DonorToken" => "",
192
+ "COFId" => "",
193
+ "DonorFirstName" => "",
194
+ "DonorLastName" => "",
195
+ "DonorEmail" => "",
196
+ "DonorAddress1" => "",
197
+ "DonorAddress2" => "",
198
+ "DonorCity" => "",
199
+ "DonorState" => "",
200
+ "DonorZip" => "",
201
+ "DonorCountry" => "",
202
+ "DonorPhone" => "",
203
+ "CardType" => "", # Unk, Visa, Mastercard, Amex or Discover
204
+ "NameOnCard" => "",
205
+ "CardNumber" => "",
206
+ "ExpMonth" => "",
207
+ "ExpYear" => "",
208
+ "CSC" => ""
209
+ })
210
+
211
+ **_Response on Success_**
212
+
213
+ {
214
+ "StatusCode" => "Success",
215
+ "Message" => "", # Empty
216
+ "ErrorDetails" => "", # Empty
217
+ "CallDuration" => "x.x",
218
+ "ChargeId" => "x", # Integer
219
+ "COFId" => "0"
220
+ }
221
+
222
+ ### GetFee
223
+
224
+ Calculates the fee for a transaction that would ultimately be used with payment functions.
225
+
226
+ **_Call_**
227
+
228
+ response = nfg_client.get_fee({
229
+ "DonationLineItems" => {
230
+ "DonationItem1" => {
231
+ "NpoEin" => "",
232
+ "donorVis" => "", # ProvideAll, ProvideNameAndEmailOnly or Anonymous
233
+ "ItemAmount" => "",
234
+ "RecurType" => "", # NotRecurring, Monthly, Quarterly or Annually
235
+ "Designation" => ""
236
+ "Dedication" => ""
237
+ },
238
+ "DonationItem2" => {
239
+ ...
240
+ },
241
+ ...
242
+ },
243
+ "TipAmount" => "",
244
+ "CardType" => ""
245
+ })
246
+
247
+ **_Response on Success_**
248
+
249
+ {
250
+ "Message" => "", # Empty
251
+ "ErrorDetails" => "", # Empty
252
+ "CallDuration" => "x.x",
253
+ "TotalChargeAmount" => "x.x",
254
+ "TotalAddFee" => "x.x",
255
+ "TotalDeductFee" => "x.x",
256
+ "TipAmount" => "x.x"
257
+ }
258
+
259
+ ### GetDonorDonationHistory
260
+
261
+ Gets donor transactions.
262
+
263
+ **_Call_**
264
+
265
+ response = nfg_client.get_donor_donation_history({
266
+ "DonorToken" => ""
267
+ })
268
+
269
+ **_Response on Success_**
270
+
271
+ {
272
+ "StatusCode" => "Success",
273
+ "Message" => "", # Empty
274
+ "ErrorDetails" => "", # Empty
275
+ "CallDuration" => "x.x",
276
+ "DonorToken" => "x",
277
+ "Donations"=>[
278
+ {
279
+ "DonationDate" => "xxxx-xx-xxTxx:xx:xx.xx",
280
+ "RecurType" => "x", # e.g. NotRecurring
281
+ "IsTpcAddOnFee" => "", # true or false
282
+ "NpoName" => "", # e.g. Smithsonian Institution
283
+ "Designation" => "",
284
+ "Dedication" => "",
285
+ "Amount" => "x.x",
286
+ "ChargeId" => "x" # Integer
287
+ },
288
+ ...
289
+ ]
290
+ }
291
+
292
+ ### GetDonationReport
293
+
294
+ Returns a report of partner transactions during a specific period of time.
295
+
296
+ **_Call_**
297
+
298
+ response = nfg_client.get_donation_report({
299
+ "StartDate" => "",
300
+ "EndDate" => "",
301
+ "DonationReportType" => "" # All, OneTime or Recurring
302
+ })
303
+
304
+ **_Response on Success_**
305
+
306
+ {
307
+ "StatusCode" => "Success",
308
+ "Message" => "", # Empty
309
+ "ErrorDetails" => "", # Empty
310
+ "CallDuration" => "x.x",
311
+ "ReturnCount" => "x",
312
+ "ReportResults"=>[
313
+ {
314
+ "Source" => "x",
315
+ "Campaign" => "x",
316
+ "ChargeId" => "x", # Integer
317
+ "ShareWithCharity" => "x", # e.g. No Donor Info - Anonymous
318
+ "DonorEmail" => "x",
319
+ "DonorFirstName" => "x",
320
+ "DonorLastName" => "x",
321
+ "DonorAddress1" => "x",
322
+ "DonorAddress2" => "x",
323
+ "DonorCity" => "x",
324
+ "DonorState" => "x",
325
+ "DonorZip" => "x",
326
+ "DonorCountry" => "x",
327
+ "DonorPhone" => "x",
328
+ "Designation" => "",
329
+ "DonateInName" => "",
330
+ "RecurringPeriod" => "",
331
+ "EventDate" => "xxxx-xx-xxTxx:xx:xx.xxxZ",
332
+ "NpoEIN" => "x",
333
+ "NpoName" => "x",
334
+ "ChargeStatus" => "", # e.g. CS_SUCCESS
335
+ "ChargeAmount" => "x.x",
336
+ "NpoTPC" => "x.x",
337
+ "DonationItemAmount" => "x.x",
338
+ "TotalDonationAmount" => "x.x",
339
+ "HistoryRecordId" => "x"
340
+ },
341
+ ...
342
+ ]
343
+ }
344
+
345
+ ## Failed calls
346
+
347
+ When a call fails (i.e. the "StatusCode" is different than "Success"), it's usually possible to find out more about the error looking at the "Message" and "ErrorDetails" fields. "Message" is a string, while "ErrorDetails" is a hash with the following format:
348
+
349
+ {
350
+ "StatusCode" => "Other",
351
+ "Message" => "x",
352
+ "ErrorDetails" => {
353
+ "ErrorInfo"=>{
354
+ "ErrCode" => "x",
355
+ "ErrData" => "x"
356
+ }
357
+ }
358
+ "CallDuration" => "x.x",
359
+ ...
360
+ }
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ desc 'Run specs'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ desc 'Default: run specs.'
8
+ task :default => :spec
@@ -0,0 +1,3 @@
1
+ require 'nfg-client/version'
2
+ require 'nfg-client/utils'
3
+ require 'nfg-client/client'
@@ -0,0 +1,321 @@
1
+ module NFGClient
2
+
3
+ def self.new(partner_id, partner_password, partner_source, partner_campaign, use_sandbox = false)
4
+ NFGClient::Client.new(partner_id, partner_password, partner_source, partner_campaign, use_sandbox)
5
+ end
6
+
7
+ class Client
8
+ include NFGClient::Utils
9
+
10
+ def initialize(partner_id, partner_password, partner_source, partner_campaign, use_sandbox)
11
+ @partner_id = partner_id
12
+ @partner_password = partner_password
13
+ @partner_source = partner_source
14
+ @partner_campaign = partner_campaign
15
+ @use_sandbox = use_sandbox
16
+ end
17
+
18
+ # Creates a card on file for the given donor token
19
+ #
20
+ # Arguments:
21
+ # params: (Hash)
22
+ def create_cof(params)
23
+ call_params = add_credentials_to_params(params)
24
+ response = nfg_soap_request('CreateCOF', call_params, @use_sandbox)
25
+ if response.is_a? REXML::Element
26
+ {
27
+ 'StatusCode' => response.elements['StatusCode'].get_text.to_s,
28
+ 'Message' => response.elements['Message'].get_text.to_s,
29
+ 'ErrorDetails' => response.elements['ErrorDetails'].get_text.to_s,
30
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s,
31
+ 'DonorToken' => response.elements['DonorToken'].get_text.to_s,
32
+ 'COFId' => response.elements['CofId'].get_text.to_s
33
+ }
34
+ else
35
+ response
36
+ end
37
+ end
38
+
39
+ # Deletes a card on file for the given donor token
40
+ #
41
+ # Arguments:
42
+ # params: (Hash)
43
+ def delete_donor_cof(params)
44
+ call_params = add_credentials_to_params(params)
45
+ response = nfg_soap_request('DeleteDonorCOF', call_params, @use_sandbox)
46
+ if response.is_a? REXML::Element
47
+ if response.elements['StatusCode'].get_text.to_s == 'Success'
48
+ {
49
+ 'StatusCode' => response.elements['StatusCode'].get_text.to_s,
50
+ 'Message' => response.elements['Message'].get_text.to_s,
51
+ 'ErrorDetails' => response.elements['ErrorDetails'].get_text.to_s,
52
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s
53
+ }
54
+ else
55
+ {
56
+ 'StatusCode' => response.elements['StatusCode'].get_text.to_s,
57
+ 'Message' => response.elements['Message'].get_text.to_s,
58
+ 'ErrorDetails' => {
59
+ 'ErrorInfo' => {
60
+ 'ErrCode' => response.elements['ErrorDetails'].andand.elements.andand['ErrorInfo'].andand.elements.andand['ErrCode'].andand.get_text.andand.to_s,
61
+ 'ErrData' => response.elements['ErrorDetails'].andand.elements.andand['ErrorInfo'].andand.elements.andand['ErrData'].andand.get_text.andand.to_s
62
+ }
63
+ },
64
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s
65
+ }
66
+ end
67
+ else
68
+ response
69
+ end
70
+ end
71
+
72
+ # Gets a list of the given donor token's cards on file
73
+ #
74
+ # Arguments:
75
+ # params: (Hash)
76
+ def get_donor_cofs(params)
77
+ call_params = add_credentials_to_params(params)
78
+ response = nfg_soap_request('GetDonorCOFs', call_params, @use_sandbox)
79
+ if response.is_a? REXML::Element
80
+ response_hash = {
81
+ 'StatusCode' => response.elements['StatusCode'].get_text.to_s,
82
+ 'Message' => response.elements['Message'].get_text.to_s,
83
+ 'ErrorDetails' => response.elements['ErrorDetails'].get_text.to_s,
84
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s,
85
+ 'DonorToken' => response.elements['DonorToken'].get_text.to_s
86
+ }
87
+ response_hash['Cards'] = Array.new
88
+ response.elements.each('Cards/COFRecord') do |card|
89
+ response_hash['Cards'] << {
90
+ 'COFId' => card.elements['COFId'].get_text.to_s,
91
+ 'CardType' => card.elements['CardType'].get_text.to_s,
92
+ 'CCSuffix' => card.elements['CCSuffix'].get_text.to_s,
93
+ 'CCExpMonth' => card.elements['CCExpMonth'].get_text.to_s,
94
+ 'CCExpYear' => card.elements['CCExpYear'].get_text.to_s,
95
+ 'bInUseByLiveRD' => card.elements['bInUseByLiveRD'].get_text.to_s,
96
+ 'COFEmailAddress' => card.elements['COFEmailAddress'].get_text.to_s
97
+ }
98
+ end
99
+ response_hash
100
+ else
101
+ response
102
+ end
103
+ end
104
+
105
+ # Calculates fee tied to transaction
106
+ #
107
+ # Arguments:
108
+ # params: (Hash)
109
+ def get_fee(params)
110
+ call_params = add_credentials_to_params(params)
111
+ response = nfg_soap_request('GetFee', call_params, @use_sandbox)
112
+ if response.is_a? REXML::Element
113
+ if response.elements['ErrorDetails'].elements['ErrorInfo'].nil?
114
+ {
115
+ 'Message' => response.elements['Message'].get_text.to_s,
116
+ 'ErrorDetails' => response.elements['ErrorDetails'].get_text.to_s,
117
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s,
118
+ 'TotalChargeAmount' => response.elements['TotalChargeAmount'].get_text.to_s,
119
+ 'TotalAddFee' => response.elements['TotalAddFee'].get_text.to_s,
120
+ 'TotalDeductFee' => response.elements['TotalChargeAmount'].get_text.to_s,
121
+ 'TipAmount' => response.elements['TotalAddFee'].get_text.to_s
122
+ }
123
+ else
124
+ {
125
+ 'Message' => response.elements['Message'].get_text.to_s,
126
+ 'ErrorDetails' => {
127
+ 'ErrorInfo' => {
128
+ 'ErrCode' => response.elements['ErrorDetails'].andand.elements.andand['ErrorInfo'].andand.elements.andand['ErrCode'].andand.get_text.andand.to_s,
129
+ 'ErrData' => response.elements['ErrorDetails'].andand.elements.andand['ErrorInfo'].andand.elements.andand['ErrData'].andand.get_text.andand.to_s
130
+ }
131
+ },
132
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s,
133
+ 'TotalChargeAmount' => response.elements['TotalChargeAmount'].get_text.to_s,
134
+ 'TotalAddFee' => response.elements['TotalAddFee'].get_text.to_s,
135
+ 'TotalDeductFee' => response.elements['TotalChargeAmount'].get_text.to_s,
136
+ 'TipAmount' => response.elements['TotalAddFee'].get_text.to_s
137
+ }
138
+ end
139
+ else
140
+ response
141
+ end
142
+ end
143
+
144
+ # Makes a donation using the given COF
145
+ #
146
+ # Arguments:
147
+ # params: (Hash)
148
+ def make_cof_donation(params)
149
+ call_params = add_credentials_to_params(params)
150
+ response = nfg_soap_request('MakeCOFDonation', call_params, @use_sandbox)
151
+ if response.is_a? REXML::Element
152
+ if response.elements['StatusCode'].get_text.to_s == 'Success'
153
+ {
154
+ 'StatusCode' => response.elements['StatusCode'].get_text.to_s,
155
+ 'Message' => response.elements['Message'].get_text.to_s,
156
+ 'ErrorDetails' => response.elements['ErrorDetails'].get_text.to_s,
157
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s,
158
+ 'ChargeId' => response.elements['ChargeId'].get_text.to_s,
159
+ 'COFId' => response.elements['CofId'].get_text.to_s
160
+ }
161
+ else
162
+ {
163
+ 'StatusCode' => response.elements['StatusCode'].get_text.to_s,
164
+ 'Message' => response.elements['Message'].get_text.to_s,
165
+ 'ErrorDetails' => {
166
+ 'ErrorInfo' => {
167
+ 'ErrCode' => response.elements['ErrorDetails'].andand.elements.andand['ErrorInfo'].andand.elements.andand['ErrCode'].andand.get_text.andand.to_s,
168
+ 'ErrData' => response.elements['ErrorDetails'].andand.elements.andand['ErrorInfo'].andand.elements.andand['ErrData'].andand.get_text.andand.to_s
169
+ }
170
+ },
171
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s,
172
+ 'ChargeId' => response.elements['ChargeId'].get_text.to_s,
173
+ 'COFId' => response.elements['CofId'].get_text.to_s
174
+ }
175
+ end
176
+ else
177
+ response
178
+ end
179
+ end
180
+
181
+ # Makes a donation and stores a COF for the given donor token
182
+ #
183
+ # Arguments:
184
+ # params: (Hash)
185
+ def make_donation_add_cof(params)
186
+ call_params = add_credentials_to_params(params)
187
+ response = nfg_soap_request('MakeDonationAddCOF', call_params, @use_sandbox)
188
+ if response.is_a? REXML::Element
189
+ if (response.elements['StatusCode'].get_text.to_s == 'Success') || ((response.elements['StatusCode'].get_text.to_s != 'Success') && response.elements['ErrorDetails'].elements['ErrorInfo'].nil?)
190
+ {
191
+ 'StatusCode' => response.elements['StatusCode'].get_text.to_s,
192
+ 'Message' => response.elements['Message'].get_text.to_s,
193
+ 'ErrorDetails' => response.elements['ErrorDetails'].get_text.to_s,
194
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s,
195
+ 'ChargeId' => response.elements['ChargeId'].get_text.to_s,
196
+ 'COFId' => response.elements['CofId'].get_text.to_s
197
+ }
198
+ else
199
+ {
200
+ 'StatusCode' => response.elements['StatusCode'].get_text.to_s,
201
+ 'Message' => response.elements['Message'].get_text.to_s,
202
+ 'ErrorDetails' => {
203
+ 'ErrorInfo' => {
204
+ 'ErrCode' => response.elements['ErrorDetails'].andand.elements.andand['ErrorInfo'].andand.elements.andand['ErrCode'].andand.get_text.andand.to_s,
205
+ 'ErrData' => response.elements['ErrorDetails'].andand.elements.andand['ErrorInfo'].andand.elements.andand['ErrData'].andand.get_text.andand.to_s
206
+ }
207
+ },
208
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s,
209
+ 'ChargeId' => response.elements['ChargeId'].get_text.to_s,
210
+ 'COFId' => response.elements['CofId'].get_text.to_s
211
+ }
212
+ end
213
+ else
214
+ response
215
+ end
216
+ end
217
+
218
+ # Retrieves the donation history for a specified donor token
219
+ #
220
+ # Arguments:
221
+ # params: (Hash)
222
+ def get_donor_donation_history(params)
223
+ call_params = add_credentials_to_params(params)
224
+ response = nfg_soap_request('GetDonorDonationHistory', call_params, @use_sandbox)
225
+ if response.is_a? REXML::Element
226
+ response_hash = {
227
+ 'StatusCode' => response.elements['StatusCode'].get_text.to_s,
228
+ 'Message' => response.elements['Message'].get_text.to_s,
229
+ 'ErrorDetails' => response.elements['ErrorDetails'].get_text.to_s,
230
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s,
231
+ 'DonorToken' => response.elements['DonorToken'].get_text.to_s
232
+ }
233
+ response_hash['Donations'] = Array.new
234
+ response.elements.each('Donations/DonationItemData') do |donation|
235
+ response_hash['Donations'] << {
236
+ 'DonationDate' => donation.elements['DonationDate'].get_text.to_s,
237
+ 'RecurType' => donation.elements['RecurType'].get_text.to_s,
238
+ 'IsTpcAddOnFee' => donation.elements['IsTpcAddOnFee'].get_text.to_s,
239
+ 'NpoName' => donation.elements['NpoName'].get_text.to_s,
240
+ 'Designation' => donation.elements['Designation'].get_text.to_s,
241
+ 'Dedication' => donation.elements['Dedication'].get_text.to_s,
242
+ 'Amount' => donation.elements['Amount'].get_text.to_s,
243
+ 'ChargeId' => donation.elements['ChargeId'].get_text.to_s,
244
+ }
245
+ end
246
+ response_hash
247
+ else
248
+ response
249
+ end
250
+ end
251
+
252
+ # Retrieves the donation history by date
253
+ #
254
+ # Arguments:
255
+ # params: (Hash)
256
+ def get_donation_report(params)
257
+ call_params = add_credentials_to_params(params)
258
+ response = nfg_soap_request('GetDonationReport', call_params, @use_sandbox)
259
+ if response.is_a? REXML::Element
260
+ response_hash = {
261
+ 'StatusCode' => response.elements['StatusCode'].get_text.to_s,
262
+ 'Message' => response.elements['Message'].get_text.to_s,
263
+ 'ErrorDetails' => response.elements['ErrorDetails'].get_text.to_s,
264
+ 'CallDuration' => response.elements['CallDuration'].get_text.to_s,
265
+ 'ReturnCount' => response.elements['ReturnCount'].get_text.to_s
266
+ }
267
+ response_hash['ReportResults'] = Array.new
268
+ response.elements.each('ReportResults/DonationReportResult') do |donation|
269
+ response_hash['ReportResults'] << {
270
+ 'Source' => donation.elements['Source'].get_text.to_s,
271
+ 'Campaign' => donation.elements['Campaign'].get_text.to_s,
272
+ 'ChargeId' => donation.elements['ChargeId'].get_text.to_s,
273
+ 'ShareWithCharity' => donation.elements['ShareWithCharity'].get_text.to_s,
274
+ 'DonorEmail' => donation.elements['DonorEmail'].get_text.to_s,
275
+ 'DonorFirstName' => donation.elements['DonorFirstName'].get_text.to_s,
276
+ 'DonorLastName' => donation.elements['DonorLastName'].get_text.to_s,
277
+ 'DonorAddress1' => donation.elements['DonorAddress1'].get_text.to_s,
278
+ 'DonorAddress2' => donation.elements['DonorAddress2'].get_text.to_s,
279
+ 'DonorCity' => donation.elements['DonorCity'].get_text.to_s,
280
+ 'DonorState' => donation.elements['DonorState'].get_text.to_s,
281
+ 'DonorZip' => donation.elements['DonorZip'].get_text.to_s,
282
+ 'DonorCountry' => donation.elements['DonorCountry'].get_text.to_s,
283
+ 'DonorPhone' => donation.elements['DonorPhone'].get_text.to_s,
284
+ 'Designation' => donation.elements['Designation'].get_text.to_s,
285
+ 'DonateInName' => donation.elements['DonateInName'].get_text.to_s,
286
+ 'RecurringPeriod' => donation.elements['RecurringPeriod'].get_text.to_s,
287
+ 'EventDate' => donation.elements['EventDate'].get_text.to_s,
288
+ 'NpoEIN' => donation.elements['NpoEIN'].get_text.to_s,
289
+ 'NpoName' => donation.elements['NpoName'].get_text.to_s,
290
+ 'ChargeStatus' => donation.elements['ChargeStatus'].get_text.to_s,
291
+ 'ChargeAmount' => donation.elements['ChargeAmount'].get_text.to_s,
292
+ 'NpoTPC' => donation.elements['NpoTPC'].get_text.to_s,
293
+ 'DonationItemAmount' => donation.elements['DonationItemAmount'].get_text.to_s,
294
+ 'TotalDonationAmount' => donation.elements['TotalDonationAmount'].get_text.to_s,
295
+ 'HistoryRecordId' => donation.elements['HistoryRecordId'].get_text.to_s
296
+ }
297
+ end
298
+ response_hash
299
+ else
300
+ response
301
+ end
302
+ end
303
+
304
+ private
305
+
306
+ # Adds client credentials to hash of parameters
307
+ #
308
+ # Arguments:
309
+ # params: (Hash)
310
+ def add_credentials_to_params(params)
311
+ return params unless params.is_a? Hash
312
+ credentials = {
313
+ 'PartnerID' => @partner_id,
314
+ 'PartnerPW' => @partner_password,
315
+ 'PartnerSource' => @partner_source,
316
+ 'PartnerCampaign' => @partner_campaign
317
+ }
318
+ credentials.merge(params)
319
+ end
320
+ end
321
+ end
@@ -0,0 +1,91 @@
1
+ require 'net/http'
2
+ require 'rexml/document'
3
+
4
+ module NFGClient
5
+ module Utils
6
+ @@nfg_urls = {
7
+ 'sandbox' => {
8
+ 'host' => 'api-sandbox.networkforgood.org',
9
+ 'url' => 'https://api-sandbox.networkforgood.org/PartnerDonationService/DonationServices.asmx',
10
+ 'wsdl' => 'https://api-sandbox.networkforgood.org/PartnerDonationService/DonationServices.asmx?wsdl'
11
+ },
12
+ 'production' => {
13
+ 'host' => 'api.networkforgood.org',
14
+ 'url' => 'https://api.networkforgood.org/PartnerDonationService/DonationServices.asmx',
15
+ 'wsdl' => 'https://api.networkforgood.org/PartnerDonationService/DonationServices.asmx?wsdl'
16
+ }
17
+ }
18
+
19
+ # Makes HTTP POST request to NFG server (sandbox or production) and returns parsed XML response.
20
+ #
21
+ # Arguments:
22
+ # nfg_method: (String)
23
+ # params: (Hash)
24
+ def nfg_soap_request(nfg_method, params, use_sandbox = false)
25
+ if (nfg_method.is_a? String) && (params.is_a? Hash)
26
+ # Build SOAP 1.2 request
27
+ soap_request = build_nfg_soap_request(nfg_method,params)
28
+
29
+ # Build request URL & header
30
+ host = use_sandbox ? @@nfg_urls['sandbox']['host'] : @@nfg_urls['production']['host']
31
+ url = use_sandbox ? @@nfg_urls['sandbox']['url'] : @@nfg_urls['production']['url']
32
+ headers = {
33
+ 'Host' => host,
34
+ 'Content-Type' => 'application/soap+xml; charset=utf-8',
35
+ 'Content-Length' => soap_request.length.to_s,
36
+ 'SOAPAction' => "#{url}/#{nfg_method}".gsub('.asmx','')
37
+ }
38
+
39
+ return_value = Hash.new
40
+
41
+ # Being HTTP Post
42
+ begin
43
+ uri = URI.parse(url)
44
+ https_conn = Net::HTTP.new(uri.host, uri.port)
45
+ https_conn.use_ssl = true
46
+ response = https_conn.post(uri.path, soap_request, headers)
47
+ if response.code == '200'
48
+ parsed = REXML::Document.new(response.body)
49
+ #return response.body
50
+ # Build return hash parsing XML response
51
+ if parsed.root.nil?
52
+ return_value['StatusCode'] = 'MissingParameter'
53
+ return_value['Message'] = response.body
54
+ return_value['ErrorDetails'] = nil
55
+ else
56
+ return_value = parsed.root.elements['soap:Body'].elements["#{nfg_method}Response"].elements["#{nfg_method}Result"]
57
+ end
58
+ else
59
+ return_value['StatusCode'] = 'UnexpectedError'
60
+ return_value['Message'] = response.message
61
+ return_value['ErrorDetails'] = response.body
62
+ end
63
+ rescue StandardError => e
64
+ return_value['StatusCode'] = 'UnexpectedError'
65
+ return_value['Message'] = e
66
+ return_value['ErrorDetails'] = e.backtrace.join(' ')
67
+ end
68
+
69
+ return_value
70
+ else
71
+ raise ArgumentError.new('http_post requires a nfg_method and a hash of params')
72
+ end
73
+ end
74
+
75
+ def build_nfg_soap_request(nfg_method, params)
76
+ get_nfg_soap_request_template.gsub('|body|',"<#{nfg_method} xmlns=\"http://api.networkforgood.org/partnerdonationservice\">#{hash_to_xml(params)}</#{nfg_method}>")
77
+ end
78
+
79
+ def get_nfg_soap_request_template
80
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\"><soap12:Body>|body|</soap12:Body></soap12:Envelope>"
81
+ end
82
+
83
+ def hash_to_xml(hash)
84
+ hash.map do |k, v|
85
+ text = (v.is_a? Hash) ? hash_to_xml(v) : v
86
+ xml_elem = (v.is_a? Hash) ? k.gsub(/(\d)/, "") : k
87
+ "<%s>%s</%s>" % [xml_elem, text, xml_elem]
88
+ end.join
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,3 @@
1
+ module NFGClient
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'nfg-client/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "nfg-client"
8
+ gem.version = NFGClient::VERSION
9
+ gem.authors = ["Antonio Ruano Cuesta"]
10
+ gem.email = ["ruanest@gmail.com"]
11
+ gem.description = %q{Client for the Network for Good SOAP API.}
12
+ gem.summary = gem.description
13
+ gem.licenses = %w(MIT)
14
+ gem.homepage = "https://github.com/aruanoc/nfg-client"
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.add_runtime_dependency 'andand', "~> 1.3.3"
22
+
23
+ gem.add_development_dependency 'rspec', '~> 2.13.0'
24
+ gem.add_development_dependency 'simplecov'
25
+ end
File without changes
@@ -0,0 +1,20 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ # This file was generated by the `rspec --init` command. Conventionally, all
5
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
6
+ # Require this file using `require "spec_helper"` to ensure that it is only
7
+ # loaded once.
8
+ #
9
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
10
+ RSpec.configure do |config|
11
+ config.treat_symbols_as_metadata_keys_with_true_values = true
12
+ config.run_all_when_everything_filtered = true
13
+ config.filter_run :focus
14
+
15
+ # Run specs in random order to surface order dependencies. If you find an
16
+ # order dependency and want to debug it, you can fix the order by providing
17
+ # the seed, which is printed after each run.
18
+ # --seed 1234
19
+ config.order = 'random'
20
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nfg-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Antonio Ruano Cuesta
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: andand
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.3.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.3.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 2.13.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 2.13.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: simplecov
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: Client for the Network for Good SOAP API.
56
+ email:
57
+ - ruanest@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .rspec
64
+ - Gemfile
65
+ - LICENSE.md
66
+ - README.md
67
+ - Rakefile
68
+ - lib/nfg-client.rb
69
+ - lib/nfg-client/client.rb
70
+ - lib/nfg-client/utils.rb
71
+ - lib/nfg-client/version.rb
72
+ - nfg-client.gemspec
73
+ - spec/nfg_client_spec.rb
74
+ - spec/spec_helper.rb
75
+ homepage: https://github.com/aruanoc/nfg-client
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.2.2
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Client for the Network for Good SOAP API.
99
+ test_files:
100
+ - spec/nfg_client_spec.rb
101
+ - spec/spec_helper.rb
102
+ has_rdoc: