My goal is to set up recurring 6 and 12 month subscriptions using "paypal" as the payment method for our SaaS. I'm using the rest API, and the one thing I can't find is a working implementation for how to take a one time sale and re-work my code into a recurring payment using PayPal's rest API (which I've read is now possible). Here's where we are at:
I have a working advanced server integration for making payments with Paypal's Express Checkout REST API.
I created the one time sale by following the code examples as explained at the above link as well as what's shown in this example from paypal. I've tried to switch out the two step process to instead include the billing-agreement creation and execution calls, but light window that opens up to have users sign in to paypal is stopping short with the error message "Things don't appear to be working at the moment. Please try again later". Here's my javascript code, which is almost exactly the same as the working example, but with different routes.
paypal.Button.render({
// Set your environment
env: 'sandbox', // sandbox | production
// Wait for the PayPal button to be clicked
payment: function(resolve, reject) {
// Make a call to the merchant server to set up the payment
console.log("button clicked")
return paypal.request.post('/payment/paypal/subscription', {amount: '0.02'})
.then(function(res) {
resolve(res.payToken); #--WHERE I'M GOING WRONG
})
.catch(function(err) { reject(err); });
},
// Wait for the payment to be authorized by the customer
onAuthorize: function(data) {
// Make a call to the merchant server to execute the payment
return paypal.request.post('/payment/paypal/execute', {
data: data,
paymentID: data.paymentID,
payerID: data.payerID
}).then(function (res) {
if (res.state == "active") {
document.querySelector('#paypal-button-container-server').innerText = 'Payment Complete!';
} else {
console.log(res);
alert("Payment could not be approved, please try again.")
}
});
}
}, '#paypal-button-container-server');
I can tell that I'm going wrong in the payment function, namely on the line with resolve(res.payToken). I don't know what piece of data from the v1/payments/billing-agreements response I should be passing to this function, but have tried both the profile_id and the approval_url (the actual href - "href": "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-XXXXXXXXXXXXX").
Where am I going wrong? If this is possible, I feel like I'm one working example away from making this work, but I'd like to know if it's not capable the way I'm doing it (in which case it might need to be done via Payflow?).
Note: I fully understand billing agreements and billing profiles, and my problems are not with the REST API. In addition to the working one-time sale implementation, I can make all necessary billing profile/agreements (verified through Postman).
Below is the response from a v1/payments/billing-agreements sandbox call in the event anyone can point out the right piece of data inside of it.
{
"name": "Magazine Subscription",
"description": "Monthly subscription with a regular monthly payment definition and two-month trial payment definition.",
"plan": {
"id": "P-XXXXXXXXXXXXXXXXXXX",
"state": "ACTIVE",
"name": "1 Month Recurring",
"description": "A recurring payment plan for customers who sign a 1-month contract",
"type": "FIXED",
"payment_definitions": [
{
"id": "PD-924GIUJ3MWQ32E22348G0018",
"name": "Regular Payment Definition",
"type": "REGULAR",
"frequency": "Month",
"amount": {
"currency": "USD",
"value": "150"
},
"cycles": "1",
"charge_models": [
{
"id": "CHM-940183BIUJ3MWQ5VK14226VH",
"type": "TAX",
"amount": {
"currency": "USD",
"value": "10"
}
}
],
"frequency_interval": "1"
},
{
"id": "PD-5604RIUJ3MWQ4Y4221782C61",
"name": "Trial Payment Definition",
"type": "TRIAL",
"frequency": "Month",
"amount": {
"currency": "USD",
"value": "120"
},
"cycles": "1",
"charge_models": [
{
"id": "CHM-640401IUJ3MWQ64W07759LB2",
"type": "TAX",
"amount": {
"currency": "USD",
"value": "10"
}
}
],
"frequency_interval": "1"
}
],
"merchant_preferences": {
"setup_fee": {
"currency": "USD",
"value": "0"
},
"max_fail_attempts": "3",
"return_url": "http://localhost:8000/payment/success",
"cancel_url": "http://localhost:8000/payment/new",
"auto_bill_amount": "NO",
"initial_fail_amount_action": "CONTINUE"
}
},
"links": [
{
"href": "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-XXXXXXXXXXXXXX",
"rel": "approval_url",
"method": "REDIRECT"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/billing-agreements/EC-XXXXXXXXXXXXX/agreement-execute",
"rel": "execute",
"method": "POST"
}
],
"start_date": "2017-12-22T09:13:49Z"
}
The REST API's still pass back the express checkout URL's but in order to use it with the checkout.js front end integration you need to pass back the token found within the approval URL. You can either parse this URL and get the token section on the server and return it back or pass it before you call resolve method.
i.e.
approval_url = {
"href": "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-XXXXXXXXXXXXX"
}
var token = "EC-XXXXXXXXXXXXX";
resolve(token);
Related
I'm integrating Klarna in a sandbox environment that is tied to Adyen. I have followed the docs precisely but am having an issue where there is no "continue" button showing in the widget thus blocking the checkout process.
-EXPECTED-
-ACTUAL (MISSING BUTTON)-
Code to re create:
Post to: https://checkout-test.adyen.com/v67/payments
{
"merchantAccount": "MERCHANTLLC",
"reference": "123",
"paymentMethod": {
"type": "klarna_account"
},
"amount": {
"currency": "USD",
"value": 18210
},
"shopperLocale": "en_US",
"countryCode": "US",
"telephoneNumber": "1111111111",
"shopperEmail": "test#gmail.com",
"shopperName": {
"firstName": "John",
"lastName": "Doe"
},
"returnUrl": "http://someurl.com",
"lineItems": [
{
"quantity": 1,
"amountExcludingTax": "16900",
"taxPercentage": "775",
"description": "asdfasdf",
"id": "123",
"taxAmount": 1310,
"amountIncludingTax": "18210",
"productUrl": "http://producturl.com"
}
]
}
which returns the client_token: "ABC123". That token is then used to load the widget via the klarna docs:
window['Klarna']['Payments'].init({
client_token: 'ABC123'
})
window['Klarna']['Payments'].load({
container: '#klarna-payments-container',
payment_method_category: 'pay_over_time',
instance_id: "klarna-payments-instance"
},
function (res) {
console.log(res);
})
What is returned from the callback is {show_form: true} and the widget loads with the ACTUAL (MISSING BUTTON) picture above. What am i missing here?? I don't understand why I don't have a continue button showing up in the widget. This button is what then authorizes the user in Klarna, takes billing info, etc. Any help would be greatly appreciated! The goal is to get the 'Continue' button showing.
Answering my own question after speaking to the Klarna team. On the dev side you need to create your own button and call the 'Authorize' endpoint. Documentation can be found here: https://developers.klarna.com/documentation/klarna-payments/integration-guide/authorize/
In Razorpay I have created an order with some amount, and also completed the payment transaction for the order, but now I need to transfer the amount to the linked account using the route, but the issue is when I use their transfer with order API from postman(which they provided in the docs). It's giving an error. What do I need to do in order to link it to the transfer and then complete the payment?
{
"error": {
"code": "BAD_REQUEST_ERROR",
"description": "The amount field is required.",
"source": "business",
"step": "payment_initiation",
"reason": "input_validation_failed",
"metadata": {},
"field": "amount"
}
}
But I have provided the amount field in the body of the request, here is what the body looks like
{
"amount": 10000, // this is the smallest unit(also tried with the larger unit)
"currency":"INR",
"transfers": [
{
"account": "acc_hhsk",
"amount": 10000,
"currency": "INR",
"notes": {
"branch": "Chess Program",
"name": "Nilesh Kumar"
},
"linked_account_notes": [
"branch"
],
"on_hold": false,
"on_hold_until": null
}
]
}
Before this, I have successfully generated the order_id. Please help me find how to link order_id with the transfer.
Pass like below it will work.
use Razorpay\Api\Api;
$transfer['transfers'][0]['account'] = "acc_IGd0hn6ZZ6khPr";
$transfer['transfers'][0]['amount'] = "800000";
$transfer['transfers'][0]['currency'] = "INR";
$notes['name'] = "sshhss";
$notes['roll_no'] = "IEC2011025";
$transfer['transfers'][0]['notes'] = $notes;
$transfer['transfers'][0]['linked_account_notes'] = ['roll_no'];
$transfer['transfers'][0]['on_hold'] = "1";
// $transfer['transfers'][0]['on_hold_until'] = "1671222870";
$paymentId = "Pay_heeu7333gg";
$api = new Api('API_KEY', 'API_SECRET');
$payment = $api->payment->fetch($paymentId)->transfer($transfer);
So, I've started playing with the Asterisk Restful Interface (ARI).
I have created a separate express app to do this.
I have a correctly configured instance of Asterisk 13 running. I know this because When I go to https://192.168.46.122:8088/ari/sounds in my browser, I am prompted for a username and password, which when entered, returns a valid JSON object back with the expected data...
[
{
"id": "conf-now-unmuted",
"text": "The conference is now unmuted.",
"formats": [
{
"language": "en",
"format": "gsm"
}
]
},
{
"id": "vm-nomore",
"text": "No more messages.",
"formats": [
{
"language": "en",
"format": "gsm"
}
]
},
{
"id": "vm-review",
"text": "press 1 to accept this recording press 2 to listen to it press 3 to rerecord your message",
"formats": [
{
"language": "en",
"format": "gsm"
}
]
},
{
"id": "demo-echodone",
"text": "The echo test has been completed.",
"formats": [
{
"language": "en",
"format": "gsm"
}
]
},
{
"id": "confbridge-rest-talk-vol-out",
"text": "...to reset your speaking volume to the default level.",
"formats": [
{
"language": "en",
"format": "gsm"
}
]
}, ...... etc etc
In my app.js file I have included the following code...
...
var logger = require('morgan');
var client = require('ari-client');
var url = 'https://192.168.46.122:8088/ari/sounds';
var username = 'correct_username';
var password = 'correct_password';
client.connect(url, username, password, function (err, ari) {
console.log('HELLLLLLOOOOO!!');
});
...
The issue, is that the anon callback is never fired. I never see 'HELLLLLLOOOOO!!'
Can anyone shed any light on why/under what circumstances this could happen? Are there any known bugs with the module that could be causing this?
Please let me know if you need further information about config, environment etc.
Thanks guys
UPDATE
Following comments below... I have tried the following:
client.connect(url, username, password)
.then(function(ari) {
console.log('HELLLLLLOOOOO!!');
})
.catch(function(err){
console.log('ERR: ' + err);
});
AND
client.connect(url, username, password, function (err, ari) {
if(err) console.log(err);
console.log('HELLLLLLOOOOO!!');
});
No error and no 'HELLLLLOOOOOO!!' at any point :-(
UPDATE 2
Have just visited /ari/api-docs/resources.json and got the following response... so it looks like it is present.
{
"_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
"_author": "David M. Lee, II <dlee#digium.com>",
"_svn_revision": "$Revision: 430337 $",
"apiVersion": "1.7.0",
"swaggerVersion": "1.1",
"basePath": "http://192.168.46.122:8088/ari",
"apis": [
{
"path": "/api-docs/asterisk.{format}",
"description": "Asterisk resources"
},
{
"path": "/api-docs/endpoints.{format}",
"description": "Endpoint resources"
},
{
"path": "/api-docs/channels.{format}",
"description": "Channel resources"
},
{
"path": "/api-docs/bridges.{format}",
"description": "Bridge resources"
},
{
"path": "/api-docs/recordings.{format}",
"description": "Recording resources"
},
{
"path": "/api-docs/sounds.{format}",
"description": "Sound resources"
},
{
"path": "/api-docs/playbacks.{format}",
"description": "Playback control resources"
},
{
"path": "/api-docs/deviceStates.{format}",
"description": "Device state resources"
},
{
"path": "/api-docs/mailboxes.{format}",
"description": "Mailboxes resources"
},
{
"path": "/api-docs/events.{format}",
"description": "WebSocket resource"
},
{
"path": "/api-docs/applications.{format}",
"description": "Stasis application resources"
}
]
}
I'm now thinking it may be an SSL issue?!
Your connection is failing (for reasons outlined below), and because of an issue / upcoming-feature in node-ari-client, the failed connection is not logged.
The node-ari-client module uses Swagger, which expects to load a JSON schema describing an API. In the node-ari-client implementation, Swagger expects to find this JSON schema at %s//%s/ari/api-docs/resources.json.
So, the first thing to check is whether or not this exists / is accessible in your application:
https://192.168.46.122:8088/ari/api-docs/resources.json
There could be several reasons why this would not be available, but most likely the problem is authentication. You mention that when visiting your URL you are "prompted for a username and password". If your JSON schema (or any other files that need to be accessed without credentials) are behind authentication, you are going to need to rethink your application structure.
Currently, if there is a connection failure before Swagger has loaded the JSON schema, node-ari-client will fail silently. There is a Pull Request waiting which addresses this issue and would log the error, but in the meantime you should address the underlying issues which are preventing the connection.
If you can successfully access resources.json, there may be other issues with accessing resources. The URL you describe is accessing your service over https, but your resources.json file is telling Swagger to access it over regular http. To handle this, you could try:
Changing the basePath in your Swagger schema to use https:
"basePath": "https://192.168.46.122:8088/ari",
Adding a protocols field to your Swagger schema:
"protocols":["http", "https"]
Removing https
This is probably a good option in order to discover if https is the cause of the connection issue. Simply keep the Swagger schema exactly as is and try accessing / connecting to your services over http. Does this make a difference?
I am using the Rally WSAPI 2.0p5 with and the JSON return
I am looking to get fields from multiple tables in a single response. Is this possible? For example I am trying to get the User Story and also get the Iteration.State in the same data response. I know it is possible to do client side and if that is the only way. Can someone provide and example how I handle the async response to build the table (array).
Simply add State to the list of attributes included in your fetch. Rally's WSAPI will populate the value for sub objects even if the main type being queried does not have that field.
launch: function() {
var userStories = Ext.create('Rally.data.WsapiDataStore', {
model: 'HierarchicalRequirement',
fetch: ['Iteration', 'State'],
autoLoad: true,
filters: [
{
property: 'Iteration.State',
value: 'Accepted'
}
],
limit: 10000,
listeners: { load: this._onDataLoaded, scope: this }
});
}
As a follow up for my original question. I recently came across the alpha release of the Batch Query WSAPI in Rally's WSAPI documentation. I would suggest the usage of the Batch Query to retrieve multiple Object Models in a single response.
As an example to get User Stories and get the Iteration Status in a single query.
{
"stories" : "/HierarchicalRequirement?fetch=Name,Iteration,State&query=(Iteration.State = Accepted)"
}
The result is something that is more usable and doesn't require multiple queries to the server. i.e.
"Results": [{
"_rallyAPIMajor": "1",
"_rallyAPIMinor": "40",
"_ref": "https://rally1.rallydev.com/slm/webservice/x/hierarchicalrequirement/xxxxxxxx.js",
"_objectVersion": "17",
"_refObjectName": "<user role> I would like <feature> for <benifit>",
"Name": "As a <user role> I would like <feature> for <benifit>",
"Iteration": {
"_rallyAPIMajor": "1",
"_rallyAPIMinor": "40",
"_ref": "https://rally1.rallydev.com/slm/webservice/x/iteration/xxxxxxxx.js",
"_objectVersion": "4",
"_refObjectName": "Sprint #",
"Name": "Sprint #",
"State": "Accepted",
"_type": "Iteration"
},
"Project": {
"_rallyAPIMajor": "1",
"_rallyAPIMinor": "40",
"_ref": "https://rally1.rallydev.com/slm/webservice/x/project/xxxxxxxx.js",
"_refObjectName": "Name",
"_type": "Project"
},
"_type": "HierarchicalRequirement"
},
....
]
For more information and a few resources:
https://rally1.rallydev.com/slm/doc/webservice/index.jsp?version=1.40
https://rally1.rallydev.com/slm/doc/webservice/batch.jsp
https://rally1.rallydev.com/slm/doc/webservice/BatchScratchPad.jsp
My code used to work, but now I am not able to publish any actions. Everything seems to be in order but the response I get from Facebook is a JSON list of the last 25 actions I took. I know my action isn't published because I'm watching it live in a browser. I'm also trying to post these actions with a browser.
Here is my code:
https://graph.facebook.com/me/NAMESPACE:ACTION?OBJECT=http://www.example.com/pumpkinpie.html&access_token=TOKEN
The response is 25 of these:
{
"data": [
{
"id": ""****"",
"from": {
"id": ""****"",
"name": "****"
},
"start_time": "2012-05-05T04:56:38+0000",
"end_time": "2012-05-05T04:56:38+0000",
"publish_time": "2012-05-05T04:56:38+0000",
"application": {
"id": ""****"",
"name": ""****"
},
"data": {
"website": {
"id": ""****"",
"url": "****",
"type": "website",
"title": "****"
}
},
"likes": {
"count": 0
},
"comments": {
"count": 0
}
},
of course the ** are actual values...
You cannot do HTTP POST for actions in a browser, only HTTP GET, which is why you receive the list of previous actions.
Please use cURL or the Graph API Explorer to accomplish creating an action.