easy_ping 0.9.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7cd4ae3fc608c08d9dcf7c6c485f93927f2225d6
4
+ data.tar.gz: 0fb57d8dfcf98107bd7493862e0b4327acb6ec31
5
+ SHA512:
6
+ metadata.gz: 66012954677b41c3a2bd85bbcf4c35835ec05f1767f1fa1713d0201bac9ef50fd80238be5d4f85199659a72582f3d7884b2e676e4dc9212b70e8cf5f497e1f24
7
+ data.tar.gz: 5741090b288a91c5c145e8d0d1eabc7b7d176037ee7b465f00c01bf98cbed0658e33a82cd4d9239a00da955b30cd83252299a11f518463b2ad4976cb02188214
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in easy_ping.gemspec
4
+ gemspec
5
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Xiaoguang Chen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,299 @@
1
+ # EasyPing
2
+
3
+ EasyPing is an out of the box Ping++ Ruby SDK. Once installed, you're ready to set up a minimal configuration and get started using EasyPing.
4
+
5
+ **Warn UNDER DEVELOPMENT** Not ready for production purpose yet.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'easy_ping'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install easy_ping
22
+
23
+ If you prefer to use the latest code, you can build from source:
24
+
25
+ ```
26
+ gem build easy_ping.gemspec
27
+ gem install easy_ping-<VERSION>.gem
28
+ ```
29
+
30
+ ## Configuration
31
+
32
+ Write these lines of code to your configuration file, for example, `easy_ping.rb`.
33
+
34
+ ```ruby
35
+ EasyPing.configure do |config|
36
+ config.app_id = 'app_Wzrrb9DW1GaLmbjn' # required
37
+ config.api_key = 'sk_test_Dq54mDyHufz9nrPeH8Hm50G8' # required
38
+ config.channel = :alipay # optional, default: :alipay, you can modify this option on runtime
39
+ end
40
+ ```
41
+
42
+ And put it under `config/initiailizers` directory of your Rails project.
43
+
44
+ Or require this file manually by yourself.
45
+
46
+ ## Usage
47
+
48
+ ```ruby
49
+ ## Create Charge ##
50
+
51
+ # Class Method Definitions : Charge Create
52
+ EasyPing::Charge.create(order_number, amount, subject, body, options={}) -> charge object
53
+ EasyPing::Charge.create(options) -> charge object
54
+
55
+ # Alias Methods
56
+ EasyPing::Charge.create
57
+ EasyPing.charge
58
+
59
+ # Examples
60
+ charge = EasyPing::Charge.create 'order_number_1', 100, 'apple', 'one delicous big apple'
61
+ charge = EasyPing.charge({
62
+ order_number: 'order_number_2',
63
+ amount: 100,
64
+ subject: 'apple',
65
+ body: 'one delicous big apple',
66
+ app_id: 'app_Wzrrb9DW1GaLmbjn',
67
+ metadata: { color: 'red'},
68
+ })
69
+
70
+
71
+ ## Retrieve Single Charge ##
72
+
73
+ # Class Method Definitions : Charge Find
74
+ EasyPing::Charge.find(charge_id) -> charge object
75
+ EasyPing::Charge.find(charge_id: charge_id) -> charge object
76
+
77
+ # Alias Methods
78
+ EasyPing::Charge.find
79
+ EasyPing.find
80
+ EasyPing.find_charge
81
+
82
+ # Examples
83
+ charge = EasyPing::Charge.find 'ch_8OG4WDTe10q1q5G8aL8aDSmH'
84
+ charge = EasyPing.find charge_id: 'ch_8OG4WDTe10q1q5G8aL8aDSmH'
85
+
86
+
87
+ ## Retrieve Charge List ##
88
+
89
+ # Class Method Definitions : Charge ALL
90
+ EasyPing::Charge.all(options={}) -> charge object list
91
+
92
+ # Alias Methods
93
+ EasyPing::Charge.all
94
+ EasyPing.all
95
+ EasyPing.all_charges
96
+ EasyPing.get_charge_list
97
+
98
+ # Examples
99
+ charges = EasyPing::Charge.all
100
+ charges = EasyPing.all {
101
+ limit: 10,
102
+ offset: 'ch_8OG4WDTe10q1q5G8aL8aDSmH', # offset & starting_after, synonym
103
+ paid: true,
104
+ refunded: false
105
+ }
106
+
107
+ # Instance Method Definitions : Charge Pagination
108
+ charges.get_next_page(options={}) -> charge object list
109
+
110
+ # Similar Methods
111
+ charges.get_next_page
112
+ charges.get_next_page! # change charges itself
113
+ charges.get_prev_page
114
+ charges.get_prev_page! # same above
115
+
116
+ # Examples
117
+ new_charges = charges.get_next_page # note: ending_before option will be omitted
118
+ charges.get_prev_page!(limit: 5) # note: starting_after option will be omitted
119
+
120
+
121
+ ## Create Refund ##
122
+
123
+ # Class Method Definitions : Refund Create
124
+ EasyPing::Refund.create(description, charge_id) -> refund object
125
+ EasyPing::Refund.create(amount, description, charge_id) -> refund object
126
+ EasyPing::Refund.create(options) -> refund object
127
+
128
+ # Alias Methods
129
+ EasyPing::Refund.create
130
+ EasyPing.refund
131
+
132
+ # Examples
133
+ EasyPing::Refund.create 'refund description', 'ch_0ijQi5LKqT5sEiOePOKWb1mF'
134
+ EasyPing.refund({
135
+ amount: 50,
136
+ description: 'refund description',
137
+ charge_id: 'ch_0ijQi5LKqT5sEiOePOKWb1mF'
138
+ })
139
+
140
+ # Instance Method Definitions : Refund Create
141
+ charge.refund(amount description) -> refund object
142
+ charge.refund(description) -> refund object
143
+ charge.refund(options) -> refund object
144
+
145
+ # Examples
146
+ charge = EasyPing::Charge.find 'ch_0ijQi5LKqT5sEiOePOKWb1mF'
147
+ refund = charge.refund 10, 'refund description'
148
+
149
+
150
+ ## Retrieve Single Refund ##
151
+
152
+ # Class Method Definitions : Refund Find
153
+ EasyPing::Refund.find(charge_id, refund_id) -> refund object
154
+ EasyPing::Refund.find(options) -> refund object
155
+
156
+ # Alias Methods
157
+ EasyPing::Refund.find
158
+ EasyPing.find
159
+ EasyPing.find_refund
160
+
161
+ # Special Note:
162
+ # Must provide both charge_id and refund_id,
163
+ # if only charge_id provided, it will retrieve a charge object
164
+
165
+ # Examples
166
+ refund = EasyPing::Refund.find 'ch_0ijQi5LKqT5sEiOePOKWb1mF', 're_TmbvDKHiXLCSG0mnj9jnDyjA'
167
+ refund = EasyPing.find_refund charge_id: 'ch_0ijQi5LKqT5sEiOePOKWb1mF', refund_id: 're_TmbvDKHiXLCSG0mnj9jnDyjA'
168
+
169
+
170
+ ## Retrieve Refund List ##
171
+
172
+ # Class Method Definitions : Refund All
173
+ EasyPing::Refund.all(charge_id, options={}) -> refund object list
174
+ EasyPing::Refund.all(options) -> refund object list
175
+
176
+ # Alias Methods
177
+ EasyPing::Refund.all
178
+ EasyPing.all_refund
179
+ EasyPing.all_refunds # in case of plural format typo
180
+ EasyPing.get_refund_list
181
+
182
+ # Examples
183
+ refund_list = EasyPing::Refund.all 'ch_0ijQi5LKqT5sEiOePOKWb1mF', { limit: 5 }
184
+ refund_list = EasyPing.all_refund charge_id: 'ch_0ijQi5LKqT5sEiOePOKWb1mF'
185
+
186
+ # Instance Method Definitions : Refund All
187
+ charge.all_refund(options={}) -> refund object list
188
+
189
+ # Alias Methods
190
+ charge.all_refund
191
+ charge.all_refunds # in case of typo
192
+ charge.get_refund_list
193
+
194
+ # Examples
195
+ charge = EasyPing::Charge.find 'ch_0ijQi5LKqT5sEiOePOKWb1mF'
196
+ refund_list = charge.all_refund limit: 5
197
+ ```
198
+
199
+ Retrieve charge or refund object from async notification is easy.
200
+ EasyPing will automatically detect whether response object is charge
201
+ or refund.
202
+
203
+ ```ruby
204
+ ## Retrieve from Async Notification ##
205
+
206
+ # Class Method Definitions : Async Notification
207
+ EasyPing.from_notification(params) -> charge/refund object
208
+
209
+ # Alias Methods
210
+ EasyPing::Charge.from_notification(params)
211
+ EasyPing::Refund.from_notification(params)
212
+
213
+ # Special Note:
214
+ # 1. params can a JSON string or a decoded hash object
215
+ # 2. it will automatically detect charge/refund object
216
+ # no matter which method you call
217
+
218
+ # Examples
219
+ charge = EasyPing::Charge.from_notification(params)
220
+ refund = EasyPing::Refund.from_notification(params)
221
+ ```
222
+
223
+ ## Advanced Usage
224
+
225
+ ```ruby
226
+ ## Runtime Setup ##
227
+
228
+ # Using different configuration from pre-setup options
229
+ ping = EasyPing.new({
230
+ app_id: 'app_Wzrrb9DW1GaLmbjn',
231
+ api_key: 'sk_test_Dq54mDyHufz9nrPeH8Hm50G8',
232
+ channel: :wx
233
+ })
234
+
235
+ # do whatever you want without change of default configuration
236
+ ping.charge 'order_number_3' 100, 'apple', 'one delicous big apple'
237
+
238
+ ## Helpers ##
239
+
240
+ # instance helpers
241
+ charge.raw # raw response body, typically json string
242
+ charge.values # response body, hash
243
+ charge.heasers # response headers
244
+ charge.status # response status, http code
245
+ charge.live? # livemode or not
246
+
247
+ # all attributes
248
+ charge.amount
249
+ charge.livemode
250
+ charge.refunded
251
+
252
+ # note: refund objects apply same rules above
253
+
254
+ ## Config ##
255
+ config = EasyPing.config
256
+
257
+ config.api_key
258
+ config.app_id
259
+ config.to_options # return config in hash format
260
+
261
+ EasyPing.config # return default config
262
+
263
+ ping = EasyPing.new({
264
+ app_id: 'app_Wzrrb9DW1GaLmbjn',
265
+ api_key: 'sk_test_Dq54mDyHufz9nrPeH8Hm50G8',
266
+ })
267
+ ping.config # return config for this ping instance
268
+
269
+ ## Available Channels ##
270
+ ["alipay", "wx", "upmp", "alipay_wap", "upmp_wap"]
271
+ ```
272
+
273
+ ## Error Handling
274
+
275
+ If fail to create or retrieve charge/refund, an error will be raised.
276
+
277
+ ```ruby
278
+ ## Error ##
279
+
280
+ begin
281
+ charge = EasyPing::Charge.find 'ch_0ijQi5LKqT5sEiOePOKWb1mF'
282
+ rescue EasyPing::APIError => e # Error return by server
283
+ puts e.message
284
+ puts e.status
285
+ puts e.type
286
+ puts e.param
287
+ rescue EasyPing::Error => e # Top level error of EasyPing
288
+ puts e.message
289
+ rescue Exception => boom
290
+ puts "something wrong with your code, #{boom.message}"
291
+ puts boom.backtrace.join("\n")
292
+ end
293
+ ```
294
+
295
+ ## Others
296
+
297
+ For Ping++ API information, please visit https://pingplusplus.com/document/api
298
+
299
+ If something doesn't work, feel free to report a bug or start an issue.
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'easy_ping/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "easy_ping"
8
+ spec.version = EasyPing::VERSION
9
+ spec.authors = ["Cxg"]
10
+ spec.email = ["xg.chen87@gmail.com"]
11
+ spec.summary = "Ruby Wrapper for Ping++ API}"
12
+ spec.description = %q{EasyPing is an out of the box Ping++ Ruby SDK.
13
+ For Ping++ API information, please
14
+ visit https://pingplusplus.com for details.}
15
+ spec.homepage = ""
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0") - %w[.gitignore]
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency 'faraday', '~> 0.9.0'
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ end
@@ -0,0 +1,29 @@
1
+ require 'json'
2
+ require 'faraday'
3
+
4
+ require 'easy_ping/version'
5
+ require 'easy_ping/error'
6
+ require 'easy_ping/utils'
7
+ require 'easy_ping/config'
8
+ require 'easy_ping/model'
9
+ require 'easy_ping/action'
10
+ require 'easy_ping/base'
11
+
12
+ module EasyPing
13
+
14
+ class << self
15
+ def new(options)
16
+ EasyPing::Base.new(options)
17
+ end
18
+
19
+ private
20
+ def method_missing(name, *args, &block)
21
+ default_proxy.send name, *args, &block
22
+ end
23
+ end
24
+
25
+ def self.default_proxy
26
+ @default_proxy ||= EasyPing::Base.new
27
+ end
28
+
29
+ end
@@ -0,0 +1,200 @@
1
+ require 'json'
2
+ module EasyPing
3
+ class Client
4
+ def initialize(api_base, api_key)
5
+ options = {ssl: {ca_file: '../ssl/ca-certificates.crt'}}
6
+ @connection = Faraday.new(api_base, options) do |conn|
7
+ conn.request :url_encoded
8
+ conn.response :logger
9
+ conn.authorization :Bearer, api_key
10
+ conn.adapter Faraday.default_adapter
11
+ end
12
+
13
+ def run(method, *args)
14
+ @connection.send method, *args
15
+ rescue Faraday::ClientError => e
16
+ raise EasyPing::HTTPClientError.new(e)
17
+ end
18
+ end
19
+ end
20
+
21
+ class Action
22
+ include EasyPing::Utils
23
+ CHANNELS = ["alipay", "wx", "upmp", "alipay_wap", "upmp_wap"]
24
+
25
+ attr_reader :client, :config
26
+
27
+ def initialize(config)
28
+ @config = config
29
+ @client = EasyPing::Client.new(config.api_base, config.api_key)
30
+ end
31
+
32
+ def from_notification(params)
33
+ EasyPing::Model::Wrapper.new(params, config)
34
+ end
35
+
36
+ extend Forwardable
37
+ # delegators to response
38
+ def_delegators :config, :live, :live?, :channel
39
+
40
+ private
41
+ def compile(options)
42
+ mappings.each do |*group|
43
+ raise ParametersInvalid, "more than one of #{group} options set" if (options.keys & group).length > 1
44
+ end
45
+ Hash[options.map {|k, v| mappings.key?(k) ? [mappings[k], v] : [k, v] }]
46
+ end
47
+
48
+ def verify!(options, requires)
49
+ missing_parameters = requires - options.keys.map(&:to_s)
50
+ if missing_parameters.length > 0
51
+ raise MissingRequiredParameters, %Q{#{missing_parameters} is required
52
+ for this action.}
53
+ end
54
+ if options['channel'] && !CHANNELS.include?(options['channel'].to_s)
55
+ raise ParametersInvalid, %Q{#{options['channel']} is not valid channel
56
+ for this action.}
57
+ end
58
+ end
59
+ end
60
+
61
+ class Refund < Action
62
+ def self.create(*args)
63
+ new(EasyPing::Base.config).refund(*args)
64
+ end
65
+
66
+ def initialize(config)
67
+ super(config)
68
+ @settings = config.to_options
69
+ end
70
+
71
+ def refund(*args)
72
+ amount = args.first
73
+ if Integer === amount
74
+ params = indifferent_params(args, 'amount', 'description', 'charge_id')
75
+ else
76
+ params = indifferent_params(args, 'description', 'charge_id')
77
+ end
78
+
79
+ # map keys to API request format and verify options
80
+ params = compile params
81
+ verify! params, refund_requires
82
+
83
+ # set up charge id for refund action
84
+ @charge_id = params.delete 'charge_id'
85
+
86
+ # run request and parse return result
87
+ raw_response = client.run(:post, api_endpoint, params)
88
+ EasyPing::Model::Wrapper.parse! raw_response, config
89
+ end
90
+
91
+ def find(*args)
92
+ params = indifferent_params(args, 'charge_id', 'refund_id')
93
+ @charge_id, @refund_id = params.values_at('charge_id', 'refund_id')
94
+
95
+ # run request and parse return result
96
+ raw_response = client.run :get, "#{api_endpoint}/#{@refund_id}"
97
+ EasyPing::Model::Wrapper.parse! raw_response, config
98
+ end
99
+
100
+ def all(*args)
101
+ params = indifferent_params(args, 'charge_id')
102
+
103
+ # map keys to API request format
104
+ params = compile params
105
+
106
+ # set up charge id for refund action
107
+ @charge_id = params.delete 'charge_id'
108
+
109
+ raw_response = client.run :get, api_endpoint, params
110
+ EasyPing::Model::Wrapper.parse! raw_response, config
111
+ end
112
+
113
+ private
114
+ def api_endpoint
115
+ "/v1/charges/#{@charge_id}/refunds"
116
+ end
117
+
118
+ def refund_requires
119
+ ['charge_id', 'description']
120
+ end
121
+
122
+ def mappings
123
+ {
124
+ 'from' => 'charge_id',
125
+ 'offset' => 'starting_after'
126
+ }
127
+ end
128
+ end
129
+
130
+ class Charge < Action
131
+ REQUIRED = [
132
+ 'order_no', 'app[id]', 'channel', 'amount', 'client_ip', 'currency',
133
+ 'subject', 'body'
134
+ ]
135
+
136
+ def self.create(*args)
137
+ new(EasyPing::Base.config).charge(*args)
138
+ end
139
+
140
+ def initialize(config)
141
+ super(config)
142
+ @settings = config.to_options.merge default_charge_options
143
+ end
144
+
145
+ def charge(*args)
146
+ params = indifferent_params(args, 'order_number', 'amount', 'subject', 'body')
147
+ params = @settings.merge params
148
+
149
+ # map keys to API request format and verify params
150
+ params = compile params
151
+ verify! params, charge_requires
152
+
153
+ # run request and parse return result
154
+ raw_response = client.run(:post, api_endpoint, params)
155
+ EasyPing::Model::Wrapper.parse! raw_response, config
156
+ end
157
+
158
+ def find(*args)
159
+ params = indifferent_params(args, 'charge_id')
160
+
161
+ raw_response = client.run :get, "#{api_endpoint}/#{params['charge_id']}"
162
+ EasyPing::Model::Wrapper.parse! raw_response, config
163
+ end
164
+
165
+ def all(params={})
166
+ params = indifferent_hash params
167
+
168
+ # map keys to API request format
169
+ params = compile params
170
+
171
+ raw_response = client.run :get, api_endpoint, params
172
+ EasyPing::Model::Wrapper.parse! raw_response, config
173
+ end
174
+
175
+ private
176
+ def api_endpoint
177
+ '/v1/charges'
178
+ end
179
+
180
+ def mappings
181
+ {
182
+ 'order_number' => 'order_no',
183
+ 'app_id' => 'app[id]',
184
+ 'app' => 'app[id]',
185
+ 'offset' => 'starting_after'
186
+ }
187
+ end
188
+
189
+ def default_charge_options
190
+ { 'client_ip' => '127.0.0.1' }
191
+ end
192
+
193
+ def charge_requires
194
+ [
195
+ 'order_no', 'app[id]', 'channel', 'amount', 'client_ip',
196
+ 'currency', 'subject', 'body'
197
+ ]
198
+ end
199
+ end
200
+ end