easy_ping 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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