didil-paypal-recurring 1.1.1
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +46 -0
- data/README.rdoc +144 -0
- data/Rakefile +5 -0
- data/docs/PP_NVPAPI_DeveloperGuide.pdf +0 -0
- data/docs/PP_WPP_IntegrationGuide.pdf +0 -0
- data/lib/paypal-recurring.rb +1 -0
- data/lib/paypal/recurring.rb +110 -0
- data/lib/paypal/recurring/base.rb +286 -0
- data/lib/paypal/recurring/cacert.pem +3987 -0
- data/lib/paypal/recurring/notification.rb +84 -0
- data/lib/paypal/recurring/request.rb +211 -0
- data/lib/paypal/recurring/response.rb +29 -0
- data/lib/paypal/recurring/response/base.rb +62 -0
- data/lib/paypal/recurring/response/checkout.rb +11 -0
- data/lib/paypal/recurring/response/details.rb +26 -0
- data/lib/paypal/recurring/response/manage_profile.rb +12 -0
- data/lib/paypal/recurring/response/payment.rb +24 -0
- data/lib/paypal/recurring/response/profile.rb +71 -0
- data/lib/paypal/recurring/response/refund.rb +23 -0
- data/lib/paypal/recurring/utils.rb +26 -0
- data/lib/paypal/recurring/version.rb +10 -0
- data/paypal-recurring.gemspec +27 -0
- data/spec/fixtures/checkout/failure.yml +38 -0
- data/spec/fixtures/checkout/success.yml +38 -0
- data/spec/fixtures/create_profile/failure.yml +38 -0
- data/spec/fixtures/create_profile/success.yml +38 -0
- data/spec/fixtures/details/cancelled.yml +38 -0
- data/spec/fixtures/details/failure.yml +38 -0
- data/spec/fixtures/details/success.yml +38 -0
- data/spec/fixtures/ipn/express_checkout.json +35 -0
- data/spec/fixtures/ipn/recurring_payment.json +44 -0
- data/spec/fixtures/ipn/recurring_payment_profile_created.json +31 -0
- data/spec/fixtures/ipn/recurring_payment_profile_created_with_initial_amount.json +33 -0
- data/spec/fixtures/ipn/recurring_payment_skipped.json +30 -0
- data/spec/fixtures/ipn/recurring_payment_with_initial_amount.json +44 -0
- data/spec/fixtures/notification/failure.yml +50 -0
- data/spec/fixtures/notification/success.yml +50 -0
- data/spec/fixtures/payment/failure.yml +38 -0
- data/spec/fixtures/payment/success.yml +38 -0
- data/spec/fixtures/profile/cancel/failure.yml +38 -0
- data/spec/fixtures/profile/cancel/success.yml +38 -0
- data/spec/fixtures/profile/failure.yml +38 -0
- data/spec/fixtures/profile/reactivate/failure.yml +38 -0
- data/spec/fixtures/profile/reactivate/success.yml +38 -0
- data/spec/fixtures/profile/success.yml +38 -0
- data/spec/fixtures/profile/suspend/failure.yml +38 -0
- data/spec/fixtures/profile/suspend/success.yml +38 -0
- data/spec/fixtures/refund/failure.yml +38 -0
- data/spec/fixtures/refund/success.yml +38 -0
- data/spec/fixtures/update_profile/failure.yml +38 -0
- data/spec/fixtures/update_profile/profile.yml +38 -0
- data/spec/fixtures/update_profile/success.yml +38 -0
- data/spec/paypal/notification_spec.rb +106 -0
- data/spec/paypal/recurring_spec.rb +81 -0
- data/spec/paypal/request_spec.rb +136 -0
- data/spec/paypal/response/base_spec.rb +19 -0
- data/spec/paypal/response/checkout_details_spec.rb +50 -0
- data/spec/paypal/response/checkout_spec.rb +32 -0
- data/spec/paypal/response/create_recurring_profile_spec.rb +41 -0
- data/spec/paypal/response/manage_profile_spec.rb +62 -0
- data/spec/paypal/response/profile_spec.rb +43 -0
- data/spec/paypal/response/refund_spec.rb +33 -0
- data/spec/paypal/response/request_payment_spec.rb +35 -0
- data/spec/paypal/response/update_recurring_profile_spec.rb +48 -0
- data/spec/spec_helper.rb +27 -0
- metadata +209 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
module PayPal
|
2
|
+
module Recurring
|
3
|
+
class Notification
|
4
|
+
extend PayPal::Recurring::Utils
|
5
|
+
|
6
|
+
attr_reader :params
|
7
|
+
|
8
|
+
mapping({
|
9
|
+
:type => :txn_type,
|
10
|
+
:transaction_id => :txn_id,
|
11
|
+
:fee => [:mc_fee, :payment_fee],
|
12
|
+
:reference => [:rp_invoice_id, :custom, :invoice],
|
13
|
+
:payment_id => :recurring_payment_id,
|
14
|
+
:amount => [:amount, :mc_gross, :payment_gross],
|
15
|
+
:currency => :mc_currency,
|
16
|
+
:status => :payment_status,
|
17
|
+
:pending_reason => :pending_reason,
|
18
|
+
:profile_status => :profile_status,
|
19
|
+
:payment_date => :payment_date,
|
20
|
+
:time_created => :time_created,
|
21
|
+
:seller_id => :receiver_id,
|
22
|
+
:email => :receiver_email,
|
23
|
+
:initial_amount => :initial_payment_amount,
|
24
|
+
:payer_email => :payer_email
|
25
|
+
})
|
26
|
+
|
27
|
+
def initialize(params = {})
|
28
|
+
self.params = params
|
29
|
+
end
|
30
|
+
|
31
|
+
def params=(params)
|
32
|
+
@params = params.inject({}) do |buffer, (name,value)|
|
33
|
+
buffer.merge(name.to_sym => value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def express_checkout?
|
38
|
+
type == "express_checkout"
|
39
|
+
end
|
40
|
+
|
41
|
+
def recurring_payment?
|
42
|
+
type == "recurring_payment"
|
43
|
+
end
|
44
|
+
|
45
|
+
def recurring_payment_profile?
|
46
|
+
type == "recurring_payment_profile_created"
|
47
|
+
end
|
48
|
+
|
49
|
+
def request
|
50
|
+
@request ||= PayPal::Recurring::Request.new.tap do |request|
|
51
|
+
request.uri = URI.parse("#{PayPal::Recurring.site_endpoint}?cmd=_notify-validate")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def response
|
56
|
+
@response ||= request.post(params.merge(:cmd => "_notify-validate"))
|
57
|
+
end
|
58
|
+
|
59
|
+
def valid?
|
60
|
+
completed? && verified? && email == PayPal::Recurring.email && seller_id == PayPal::Recurring.seller_id
|
61
|
+
end
|
62
|
+
|
63
|
+
def completed?
|
64
|
+
status == "Completed"
|
65
|
+
end
|
66
|
+
|
67
|
+
def next_payment_date
|
68
|
+
self.class.convert_to_time(params[:next_payment_date]) if params[:next_payment_date]
|
69
|
+
end
|
70
|
+
|
71
|
+
def paid_at
|
72
|
+
self.class.convert_to_time(payment_date) if payment_date
|
73
|
+
end
|
74
|
+
|
75
|
+
def created_at
|
76
|
+
self.class.convert_to_time(time_created) if time_created
|
77
|
+
end
|
78
|
+
|
79
|
+
def verified?
|
80
|
+
response.body == "VERIFIED"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
module PayPal
|
2
|
+
module Recurring
|
3
|
+
class Request
|
4
|
+
METHODS = {
|
5
|
+
:checkout => "SetExpressCheckout",
|
6
|
+
:payment => "DoExpressCheckoutPayment",
|
7
|
+
:details => "GetExpressCheckoutDetails",
|
8
|
+
:create_profile => "CreateRecurringPaymentsProfile",
|
9
|
+
:profile => "GetRecurringPaymentsProfileDetails",
|
10
|
+
:manage_profile => "ManageRecurringPaymentsProfileStatus",
|
11
|
+
:update_profile => "UpdateRecurringPaymentsProfile",
|
12
|
+
:refund => "RefundTransaction"
|
13
|
+
}
|
14
|
+
|
15
|
+
INITIAL_AMOUNT_ACTIONS = {
|
16
|
+
:cancel => "CancelOnFailure",
|
17
|
+
:continue => "ContinueOnFailure"
|
18
|
+
}
|
19
|
+
|
20
|
+
ACTIONS = {
|
21
|
+
:cancel => "Cancel",
|
22
|
+
:suspend => "Suspend",
|
23
|
+
:reactivate => "Reactivate"
|
24
|
+
}
|
25
|
+
|
26
|
+
PERIOD = {
|
27
|
+
:daily => "Day",
|
28
|
+
:weekly => "Weekly",
|
29
|
+
:monthly => "Month",
|
30
|
+
:yearly => "Year"
|
31
|
+
}
|
32
|
+
|
33
|
+
TRIAL_PERIOD = {
|
34
|
+
:daily => "Day",
|
35
|
+
:weekly => "Weekly",
|
36
|
+
:monthly => "Month",
|
37
|
+
:yearly => "Year"
|
38
|
+
}
|
39
|
+
|
40
|
+
OUTSTANDING = {
|
41
|
+
:next_billing => "AddToNextBilling",
|
42
|
+
:no_auto => "NoAutoBill"
|
43
|
+
}
|
44
|
+
|
45
|
+
REFUND_TYPE = {
|
46
|
+
:full => "Full",
|
47
|
+
:partial => "Partial",
|
48
|
+
:external => "ExternalDispute",
|
49
|
+
:other => "Other"
|
50
|
+
}
|
51
|
+
|
52
|
+
ATTRIBUTES = {
|
53
|
+
:action => "ACTION",
|
54
|
+
:amount => ["PAYMENTREQUEST_0_AMT", "AMT"],
|
55
|
+
:billing_type => "L_BILLINGTYPE0",
|
56
|
+
:cancel_url => "CANCELURL",
|
57
|
+
:currency => ["PAYMENTREQUEST_0_CURRENCYCODE", "CURRENCYCODE"],
|
58
|
+
:description => ["DESC", "PAYMENTREQUEST_0_DESC", "L_BILLINGAGREEMENTDESCRIPTION0"],
|
59
|
+
:note => "NOTE",
|
60
|
+
:item_category => "L_PAYMENTREQUEST_0_ITEMCATEGORY0",
|
61
|
+
:item_name => "L_PAYMENTREQUEST_0_NAME0",
|
62
|
+
:item_amount => "L_PAYMENTREQUEST_0_AMT0",
|
63
|
+
:item_quantity => "L_PAYMENTREQUEST_0_QTY0",
|
64
|
+
:email => "EMAIL",
|
65
|
+
:failed => "MAXFAILEDPAYMENTS",
|
66
|
+
:frequency => "BILLINGFREQUENCY",
|
67
|
+
:initial_amount => "INITAMT",
|
68
|
+
:initial_amount_action => "FAILEDINITAMTACTION",
|
69
|
+
:ipn_url => ["PAYMENTREQUEST_0_NOTIFYURL", "NOTIFYURL"],
|
70
|
+
:locale => "LOCALECODE",
|
71
|
+
:method => "METHOD",
|
72
|
+
:no_shipping => "NOSHIPPING",
|
73
|
+
:outstanding => "AUTOBILLOUTAMT",
|
74
|
+
:password => "PWD",
|
75
|
+
:payer_id => "PAYERID",
|
76
|
+
:payment_action => "PAYMENTREQUEST_0_PAYMENTACTION",
|
77
|
+
:period => "BILLINGPERIOD",
|
78
|
+
:profile_id => "PROFILEID",
|
79
|
+
:reference => ["PROFILEREFERENCE", "PAYMENTREQUEST_0_CUSTOM", "PAYMENTREQUEST_0_INVNUM"],
|
80
|
+
:refund_type => "REFUNDTYPE",
|
81
|
+
:return_url => "RETURNURL",
|
82
|
+
:signature => "SIGNATURE",
|
83
|
+
:start_at => "PROFILESTARTDATE",
|
84
|
+
:token => "TOKEN",
|
85
|
+
:transaction_id => "TRANSACTIONID",
|
86
|
+
:trial_amount => "TRIALAMT",
|
87
|
+
:trial_frequency => "TRIALBILLINGFREQUENCY",
|
88
|
+
:trial_length => "TRIALTOTALBILLINGCYCLES",
|
89
|
+
:trial_period => "TRIALBILLINGPERIOD",
|
90
|
+
:username => "USER",
|
91
|
+
:version => "VERSION",
|
92
|
+
:brand_name => "BRANDNAME",
|
93
|
+
:page_style => "PAGESTYLE",
|
94
|
+
}
|
95
|
+
|
96
|
+
CA_FILE = File.dirname(__FILE__) + "/cacert.pem"
|
97
|
+
|
98
|
+
attr_accessor :uri
|
99
|
+
|
100
|
+
# Do a POST request to PayPal API.
|
101
|
+
# The +method+ argument is the name of the API method you want to invoke.
|
102
|
+
# For instance, if you want to request a new checkout token, you may want
|
103
|
+
# to do something like:
|
104
|
+
#
|
105
|
+
# response = request.run(:express_checkout)
|
106
|
+
#
|
107
|
+
# We normalize the methods name. For a list of what's being covered, refer to
|
108
|
+
# PayPal::Recurring::Request::METHODS constant.
|
109
|
+
#
|
110
|
+
# The params hash can use normalized names. For a list, check the
|
111
|
+
# PayPal::Recurring::Request::ATTRIBUTES constant.
|
112
|
+
#
|
113
|
+
def run(method, params = {})
|
114
|
+
params = prepare_params(params.merge(:method => METHODS.fetch(method, method.to_s)))
|
115
|
+
response = post(params)
|
116
|
+
Response.process(method, response)
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
#
|
121
|
+
def request
|
122
|
+
@request ||= Net::HTTP::Post.new(uri.request_uri).tap do |http|
|
123
|
+
http["User-Agent"] = "PayPal::Recurring/#{PayPal::Recurring::Version::STRING}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
#
|
128
|
+
#
|
129
|
+
def post(params = {})
|
130
|
+
request.form_data = params
|
131
|
+
client.request(request)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Join params and normalize attribute names.
|
135
|
+
#
|
136
|
+
def prepare_params(params) # :nodoc:
|
137
|
+
normalize_params default_params.merge(params)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Parse current API url.
|
141
|
+
#
|
142
|
+
def uri # :nodoc:
|
143
|
+
@uri ||= URI.parse(PayPal::Recurring.api_endpoint)
|
144
|
+
end
|
145
|
+
|
146
|
+
def client
|
147
|
+
@client ||= begin
|
148
|
+
Net::HTTP.new(uri.host, uri.port).tap do |http|
|
149
|
+
http.use_ssl = true
|
150
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
151
|
+
http.ca_file = CA_FILE
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def default_params
|
157
|
+
{
|
158
|
+
:username => PayPal::Recurring.username,
|
159
|
+
:password => PayPal::Recurring.password,
|
160
|
+
:signature => PayPal::Recurring.signature,
|
161
|
+
:version => PayPal::Recurring.api_version
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
def normalize_params(params)
|
166
|
+
params.inject({}) do |buffer, (name, value)|
|
167
|
+
attr_names = [ATTRIBUTES[name.to_sym]].flatten.compact
|
168
|
+
attr_names << name if attr_names.empty?
|
169
|
+
|
170
|
+
attr_names.each do |attr_name|
|
171
|
+
buffer[attr_name.to_sym] = respond_to?("build_#{name}") ? send("build_#{name}", value) : value
|
172
|
+
end
|
173
|
+
|
174
|
+
buffer
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def build_period(value) # :nodoc:
|
179
|
+
PERIOD.fetch(value.to_sym, value) if value
|
180
|
+
end
|
181
|
+
|
182
|
+
def build_trial_period(value)
|
183
|
+
TRIAL_PERIOD.fetch(value.to_sym, value) if value
|
184
|
+
end
|
185
|
+
|
186
|
+
def build_start_at(value) # :nodoc:
|
187
|
+
value.respond_to?(:strftime) ? value.strftime("%Y-%m-%dT%H:%M:%SZ") : value
|
188
|
+
end
|
189
|
+
|
190
|
+
def build_outstanding(value) # :nodoc:
|
191
|
+
OUTSTANDING.fetch(value.to_sym, value) if value
|
192
|
+
end
|
193
|
+
|
194
|
+
def build_refund_type(value) # :nodoc:
|
195
|
+
REFUND_TYPE.fetch(value.to_sym, value) if value
|
196
|
+
end
|
197
|
+
|
198
|
+
def build_action(value) # :nodoc:
|
199
|
+
ACTIONS.fetch(value.to_sym, value) if value
|
200
|
+
end
|
201
|
+
|
202
|
+
def build_initial_amount_action(value) # :nodoc:
|
203
|
+
INITIAL_AMOUNT_ACTIONS.fetch(value.to_sym, value) if value
|
204
|
+
end
|
205
|
+
|
206
|
+
def build_locale(value) # :nodoc:
|
207
|
+
value.to_s.upcase
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module PayPal
|
2
|
+
module Recurring
|
3
|
+
module Response
|
4
|
+
autoload :Base, "paypal/recurring/response/base"
|
5
|
+
autoload :Checkout, "paypal/recurring/response/checkout"
|
6
|
+
autoload :Details, "paypal/recurring/response/details"
|
7
|
+
autoload :Payment, "paypal/recurring/response/payment"
|
8
|
+
autoload :ManageProfile, "paypal/recurring/response/manage_profile"
|
9
|
+
autoload :Profile, "paypal/recurring/response/profile"
|
10
|
+
autoload :Refund, "paypal/recurring/response/refund"
|
11
|
+
|
12
|
+
RESPONDERS = {
|
13
|
+
:checkout => "Checkout",
|
14
|
+
:details => "Details",
|
15
|
+
:payment => "Payment",
|
16
|
+
:profile => "Profile",
|
17
|
+
:create_profile => "ManageProfile",
|
18
|
+
:manage_profile => "ManageProfile",
|
19
|
+
:update_profile => "ManageProfile",
|
20
|
+
:refund => "Refund"
|
21
|
+
}
|
22
|
+
|
23
|
+
def self.process(method, response)
|
24
|
+
response_class = PayPal::Recurring::Response.const_get(RESPONDERS[method])
|
25
|
+
response_class.new(response)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module PayPal
|
2
|
+
module Recurring
|
3
|
+
module Response
|
4
|
+
class Base
|
5
|
+
extend PayPal::Recurring::Utils
|
6
|
+
|
7
|
+
attr_accessor :response
|
8
|
+
|
9
|
+
mapping(
|
10
|
+
:token => :TOKEN,
|
11
|
+
:ack => :ACK,
|
12
|
+
:version => :VERSION,
|
13
|
+
:build => :BUILD,
|
14
|
+
:correlaction_id => :CORRELATIONID,
|
15
|
+
:requested_at => :TIMESTAMP
|
16
|
+
)
|
17
|
+
|
18
|
+
def initialize(response = nil)
|
19
|
+
@response = response
|
20
|
+
end
|
21
|
+
|
22
|
+
def params
|
23
|
+
@params ||= CGI.parse(response.body).inject({}) do |buffer, (name, value)|
|
24
|
+
buffer.merge(name.to_sym => value.first)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def errors
|
29
|
+
@errors ||= begin
|
30
|
+
index = 0
|
31
|
+
[].tap do |errors|
|
32
|
+
while params[:"L_ERRORCODE#{index}"]
|
33
|
+
errors << {
|
34
|
+
:code => params[:"L_ERRORCODE#{index}"],
|
35
|
+
:messages => [
|
36
|
+
params[:"L_SHORTMESSAGE#{index}"],
|
37
|
+
params[:"L_LONGMESSAGE#{index}"]
|
38
|
+
]
|
39
|
+
}
|
40
|
+
|
41
|
+
index += 1
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def success?
|
48
|
+
ack == "Success"
|
49
|
+
end
|
50
|
+
|
51
|
+
def valid?
|
52
|
+
errors.empty? && success?
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def build_requested_at(stamp) # :nodoc:
|
57
|
+
Time.parse(stamp)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module PayPal
|
2
|
+
module Recurring
|
3
|
+
module Response
|
4
|
+
class Details < Base
|
5
|
+
mapping(
|
6
|
+
:status => :CHECKOUTSTATUS,
|
7
|
+
:email => :EMAIL,
|
8
|
+
:email => :EMAIL,
|
9
|
+
:payer_id => :PAYERID,
|
10
|
+
:payer_status => :PAYERSTATUS,
|
11
|
+
:first_name => :FIRSTNAME,
|
12
|
+
:last_name => :LASTNAME,
|
13
|
+
:country => :COUNTRYCODE,
|
14
|
+
:currency => :CURRENCYCODE,
|
15
|
+
:amount => :AMT,
|
16
|
+
:description => :DESC,
|
17
|
+
:ipn_url => :NOTIFYURL
|
18
|
+
)
|
19
|
+
|
20
|
+
def agreed?
|
21
|
+
params[:BILLINGAGREEMENTACCEPTEDSTATUS] == "1"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module PayPal
|
2
|
+
module Recurring
|
3
|
+
module Response
|
4
|
+
class Payment < Base
|
5
|
+
mapping(
|
6
|
+
:status => :PAYMENTINFO_0_PAYMENTSTATUS,
|
7
|
+
:amount => :PAYMENTINFO_0_AMT,
|
8
|
+
:fees => :PAYMENTINFO_0_FEEAMT,
|
9
|
+
:transaction_id => :PAYMENTINFO_0_TRANSACTIONID,
|
10
|
+
:seller_id => :PAYMENTINFO_0_SECUREMERCHANTACCOUNTID,
|
11
|
+
:reference => [:PROFILEREFERENCE, :PAYMENTREQUEST_0_CUSTOM, :PAYMENTREQUEST_0_INVNUM]
|
12
|
+
)
|
13
|
+
|
14
|
+
def completed?
|
15
|
+
params[:PAYMENTINFO_0_PAYMENTSTATUS] == "Completed"
|
16
|
+
end
|
17
|
+
|
18
|
+
def approved?
|
19
|
+
params[:PAYMENTINFO_0_ACK] == "Success"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|