Forked from LeCoupa/1-meteor-paypal-api-rest-server-file.coffee
Created
May 14, 2014 15:41
-
-
Save odesey/a7efbd1ea0d7222b6c32 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### | |
This gist illustrates how to create and execute a payment with Paypal using their REST API. | |
For additional informations, check the documentation: https://developer.paypal.com/docs/api/ | |
Note 1: I assume that you have already created a developer account for Paypal and an application. | |
To test that your code is working, use the sandbox accounts. | |
https://developer.paypal.com/webapps/developer/applications/accounts | |
Note 2: we will not use the Paypal REST API SDK package for Node.js | |
https://github.com/paypal/rest-api-sdk-nodejs | |
### | |
# The following code must be only run on the server! | |
# 1. Create your paypal configuration object. | |
@paypalConf = | |
host: "api.sandbox.paypal.com" | |
clientId: "pHQcZ1cH0lnX5Mub4MGzo_-FH6witB3_2zuRYgvUFxMHFH6wiAe3zCRDqatu3" | |
clientSecret:"Xk7-EDJqERAU5up6wjeVoRE6WM2OoIsUT3ouxVRKUmjX38b4k0-q6t_UHei" | |
# 2. Create two collections to save our payments and the Paypal tokens. | |
@PaypalPayments = new Meteor.Collection 'paypal_payments' | |
@PaypalTokens = new Meteor.Collection 'paypal_tokens' | |
# 3. Create the three methods to: | |
# - Get a valid token to make API calls. (retrieve a new one if invalid) | |
# - Create a payment | |
# - Execute a payment. | |
Meteor.methods | |
'getPaypalToken': -> | |
isTokenValid = 0 | |
token = PaypalTokens.findOne({ timestamp: { $exists: true } }, | |
{ sort: { timestamp: -1 } }) | |
if token? | |
isTokenValid = Math.ceil((new Date().getTime() - token.timestamp) / 1000) | |
# is the token invalid? | |
if isTokenValid is 0 or isTokenValid > token.expires_in | |
auth = paypalConf['clientId'] + ':' + | |
paypalConf['clientSecret'] | |
token = EJSON.parse( | |
Meteor.http.post('https://api.sandbox.paypal.com/v1/oauth2/token', | |
headers: | |
'Accept': 'application/json' | |
'Accept-Language': 'en_US' | |
auth: auth | |
params: | |
'grant_type': 'client_credentials' | |
).content) | |
token['timestamp'] = new Date().getTime() | |
# we insert the new valid token to retrieve it later | |
PaypalTokens.insert token | |
return token | |
'createPaypalPayment': (product) -> | |
token = Meteor.call 'getPaypalToken' | |
payment = | |
intent: 'sale' | |
payer: | |
payment_method: 'paypal' | |
redirect_urls: | |
return_url: 'http://localhost:3000/dashboard/payment/paypal/execute' | |
cancel_url: 'http://localhost:3000/dashboard' | |
transactions: [ | |
item_list: | |
'items': [ | |
'name': product.name, | |
'price': product.price, | |
'currency': 'USD', | |
'quantity': 1 | |
] | |
amount: | |
total: product.price | |
currency: 'USD' | |
description: product.description | |
] | |
res = Meteor.http.post 'https://api.sandbox.paypal.com/v1/payments/payment', | |
headers: | |
Authorization: 'Bearer ' + token.access_token | |
'Content-Type': 'application/json' | |
data: payment | |
res.data['userId'] = @userId | |
# we insert the payment details (for the payment id during execution) | |
PaypalPayments.insert res.data | |
return res.data | |
'executePaypalPayment': (payerId) -> | |
payment = PaypalPayments.findOne({ userId: @userId }, | |
{ sort: { 'create_time': -1 } }) | |
token = Meteor.call 'getPaypalToken' | |
url = 'https://api.sandbox.paypal.com/v1/payments/payment/' + | |
payment.id + '/execute' | |
res = Meteor.http.post url, | |
headers: | |
Authorization: 'Bearer ' + token.access_token | |
'Content-Type': 'application/json' | |
data: | |
payer_id: payerId | |
payment = res.data | |
payment['userId'] = @userId | |
if payment.state in ['approved' , 'pending'] # Be careful, in production the payment state is "pending" | |
# we insert the sucessful payment here | |
PaypalPayments.insert payment | |
return if payment.state is 'approved' then true else false | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The following code must be accessible to the client. | |
# 4. We configure Iron Router to handle the different payment steps. | |
# For more informations: https://github.com/EventedMind/iron-router | |
Router.map -> | |
@route 'DashboardRoot', | |
path: '/dashboard/' | |
@route 'DashboardPaymentPaypalExecute', | |
path: '/dashboard/payment/paypal/execute/' | |
# 5. Templates Events. | |
Template.DashboardRoot.events | |
'click .buy-product': -> | |
product = | |
name: 'product name' | |
description: 'production description' | |
price: 30.00 | |
Meteor.call 'createPaypalPayment', product, (err, res) -> | |
window.location.replace res.links[1].href | |
Template.DashboardPaymentPaypalExecute.created = -> | |
payerId = window.location.search.split('PayerID=')[1] | |
Meteor.call 'executePaypalPayment', payerId, (err, res) -> | |
if res is true | |
console.log 'Your payment has been successfully executed.' | |
else | |
console.log 'Your payment has been refused.' | |
Router.go 'DashboardRoot' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- 6. The Dashboard Template --> | |
<template name="DashboardRoot"> | |
<span class="buy-product">Buy this product</span> | |
</template> | |
<!-- 7. The Dashboard Payment Paypal Execute Template --> | |
<template name="DashboardPaymentPaypalExecute"> | |
<span class="loading">Wait a few seconds, we are processing your payment.</span> | |
</template> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment