Simulate PayPal errors in a sandbox account - javascript

I'm integrating PayPal payment on a web application I'm developing. I need to create an authorization for a transaction where I lock an amount of money (let's say 20€), then at the end of the transaction I complete the transaction and I take only the money that I need to take (so if the transaction's final cost is 15€, I give back 5€ to the user).
This workflow is currently working on a sandbox account, but now I wanted to test some errors that may occur while starting a new transaction, like for instance when the user doesn't have the sufficient amount of money (20€) that I need to lock in order to start a new transaction.
I found this documentation (https://developer.paypal.com/docs/api/test-values/#invoke-negative-testing) where it is stated To trigger the SENDER_EMAIL_UNCONFIRMED simulation response, set the items[0]/note value to ERRPYO002 in the POST v1/payments/payouts call. with the following code:
curl -X POST https://api.sandbox.paypal.com/v1/payments/payouts \
-H "content-type: application/json" \
-H "Authorization: Bearer Access-Token" \
-d '{
"sender_batch_header": {
"sender_batch_id": "1524086406556",
"email_subject": "This email is related to simulation"
},
"items": [
{
"recipient_type": "EMAIL",
"receiver": "payouts-simulator-receiver#paypal.com",
"note": "ERRPYO002",
"sender_item_id": "15240864065560",
"amount": {
"currency": "USD",
"value": "1.00"
}
}]
}'
So I guess that I need to pass an error code (like ERRPYO002) to a note field in my request body.
I'm using the checkout sdk, and my js code currently looks like this:
const buttonOpts = {
env: 'sandbox',
client: { production: $scope.key, sandbox: $scope.key },
style: {
label: 'paypal',
size: 'medium',
shape: 'rect',
color: 'blue',
tagline: false,
},
validate: actions => {
// stuff
},
payment: (data, actions) => {
return actions.payment.create({
intent: 'authorize',
payer: { payment_method: 'paypal' },
transactions: [
{
amount: {
total: '20.00',
currency: 'EUR',
},
description: 'My description',
},
],
});
},
onAuthorize: data => {
// Sending data.paymentID and data.payerID to my backend to confirm the new transaction
},
onCancel: () => {
// stuff
},
onError: err => {
console.log(err);
// stuff
},
};
Paypal.Button.render(buttonOpts, '#paypal-button');
I guess that I need to pass the code needed to simulate the error to my actions.payment.create object parameter, but I didn't find where exactly since my workflow is different that the one in the docs.
These are the codes that PayPal allows you to use for error testing:
https://developer.paypal.com/docs/payouts/integrate/test-payouts/#test-values
Any help is appreciated.
Thanks a lot.

Ok, I've actually found out how to solve this problem right after I posted this question.
I'll just put my solution here for anyone that may have this problem in the future.
The option object I posted is actually correct as it is now, so after the user confirms that he/she wants to start a new transaction I get the payerID and the paymentID to send to my backend.
On my backend function I changed my code so that it is as follows:
const paypal = require('paypal-rest-sdk');
const paymentId = event.paymentID;
const payerId = { payer_id: event.payerID };
paypal.configure({
mode: process.env.PAYPAL_ENVIRONMENT, //sandbox or live
client_id: '<MY_CLIENT_ID>',
client_secret: '<MY_CLIENT_SECRET>',
});
paypal.payment.execute(
paymentId,
payerId,
// START NEW CODE
{
headers: {
'Content-Type': 'application/json',
'PayPal-Mock-Response': '{"mock_application_codes": "INSUFFICIENT_FUNDS"}',
},
},
// END NEW CODE
(error, payment) => {
console.error(JSON.stringify(error));
console.error(JSON.stringify(payment));
if (error) {
/*
{
"response": {
"name": "INSUFFICIENT_FUNDS",
"message": "Buyer cannot pay - insufficient funds.",
"information_link": "https://developer.paypal.com/docs/api/payments/#errors",
"debug_id": "a1b2c3d4e5f6g",
"details": [
{
"issue": "The buyer must add a valid funding instrument, such as a credit card or bank account, to their PayPal account."
}
],
"httpStatusCode": 400
},
"httpStatusCode": 400
}
*/
return callback('unhandled_error', null);
}
if (payment.state === 'approved' && payment.transactions && payment.transactions[0].related_resources && payment.transactions[0].related_resources[0].authorization) {
return callback(null, payment.transactions[0].related_resources[0].authorization.id);
}
console.log('payment not successful');
return callback('unhandled_error', null);
}
);
In the request headers you just have to put an header called PayPal-Mock-Response that contains the error code you want to test, and that's it.
Hope this'll help somebody!

Related

PayPal Smart Buttons Error: "Expected order id to be passed"

i have a problem : I'm trying to figure out what it is since last friday, I can't find any good docs and no clues on the internet, I share some screenshots if anyone could help it would be awesome, by the way my problem is that when clicking on the paypal smart buttons that I integrated, i get in the console "expected an order id to be passed" unfortunately I can't find what is that order id thing
paypal.Buttons({
style: {
shape: 'rect',
color: 'gold',
layout: 'vertical',
label: 'pay',
},
// Sets up the transaction when a payment button is clicked
createOrder: function (data, actions) {
var cartArray = shoppingCart.listCart();
// Call your backend to create the Checkout Session
$.ajax({
data: {
id: cookie
},
type: "POST",
url: "create.php",
success: function (response) {
response = JSON.parse(response)
surname = []
surname = response.name.split(/(\s+)/).filter(e => e.trim().length > 0)
const Cart5 = [];
function Cart(description, name, unit_amount, quantity) {
this.description = description;
this.name = name;
this.unit_amount = {
value: unit_amount,
currency_code: 'EUR'
};
this.quantity = quantity;
this.category = 'PHYSICAL_GOODS';
}
cartArray.forEach(element => {
var item = new Cart(element.id, element.name, element.price, element.count);
Cart5.push(item);
})
JSON.stringify(Cart5);
orderid = makeid(12);
const paymentData = {
//authorization: 'AdhHUVsamn71V-Xs5JZVpzL4v6ElEKiYywV6PwF7rRwCwRQ-AZHMcLELmvQuWlS1pL19iiCbbZUIupTt',
intent: "CAPTURE",
env: "production",
application_context: {
brand_name: "Skunker",
locale: "fr-FR",
landing_page: "BILLING",
shipping_preference: "SET_PROVIDED_ADDRESS",
},
purchase_units: [{
reference_id: orderid,
custom_id: orderid,
description: 'Achat chez Skunker.Store',
invoice_id: orderid,
payer: {
email_address: response.email,
},
amount: {
currency_code: "EUR",
value: shoppingCart.totalCart() - (shoppingCart.totalCart() * sessionStorage.getItem('discount') / 100) + 6.15,
breakdown: {
item_total: {
currency_code: "EUR",
value: shoppingCart.totalCart()
},
tax_total: {
currency_code: "EUR",
value: 0.20
},
shipping: {
currency_code: "EUR",
value: 5.95
},
handling: {
currency_code: "EUR",
value: 0.00
},
insurance: {
currency_code: "EUR",
value: 0.0
},
shipping_discount: {
currency_code: "EUR",
value: 0.00
}
}
},
items: Cart5,
shipping: {
method: "La Poste - Colissimo",
address: {
name: {
given_name: surname[0],
surname: surname[1],
},
address_line_1: response.address,
admin_area_2: response.city,
postal_code: response.postalCode,
country_code: "FR"
},
phone_number: {
country_code: '33',
national_number: response.tel,
}
}
}]
}
return actions.order.create(paymentData);
}
})
},
// Finalize the transaction after payer approval
onApprove: function (data, actions) {
return actions.order.capture().then(function (orderData) {
// Full available details
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
// Show a success message within this page, e.g.
const element = document.getElementById('paypal-button-container');
element.innerHTML = '';
element.innerHTML = '<h3>Thank you for your payment!</h3>';
// Successful capture! For dev/demo purposes:
$.ajax({
data: JSON.stringify(paymentData),
type: "POST",
url: "https://skunker.store/scripts/paid.php",
success: function (response) {}
})
// When ready to go live, remove the alert and show a success message within this page. For example:
// var element = document.getElementById('paypal-button-container');
// element.innerHTML = '';
// element.innerHTML = '<h3>Thank you for your payment!</h3>';
// Or go to another URL: actions.redirect('thank_you.html');
});
},
onError: function (err) {
console.log(err);
},
}).render('#payBtn');
And this is the error I get :
Error: Expected an order id to be passed
at https://www.sandbox.paypal.com/smart/buttons?style.label=pay&style.layout=vertical&style.color=gold&style.shape=rect&style.tagline=false&style.menuPlacement=below&components.0=buttons&locale.country=FR&locale.lang=fr&sdkMeta=eyJ1cmwiOiJodHRwczovL3d3dy5wYXlwYWwuY29tL3Nkay9qcz9jbGllbnQtaWQ9QVhTMWxLcXR2cjkzbDVkYlFPTE9nclJCYlNKMWt0cmNnYkpMZnJjRWEyckhING1ZYzVvWlQ1dmxpWUZDMnRobUFlcDVic1h4RmF1WEJ1cDMmY3VycmVuY3k9RVVSIiwiYXR0cnMiOnsiZGF0YS1zZGstaW50ZWdyYXRpb24tc291cmNlIjoiYnV0dG9uLWZhY3RvcnkiLCJkYXRhLXVpZCI6InVpZF9tdmh4dGh4aHhlYW13bHJzYXVna2dqeGRmcmpqenMifX0&clientID=AXS1lKqtvr93l5dbQOLOgrRBbSJ1ktrcgbJLfrcEa2rHH4mYc5oZT5vliYFC2thmAep5bsXxFauXBup3&sdkCorrelationID=f540177905bdc&storageID=uid_dbb0ba13c9_mtq6mzg6ndi&sessionID=uid_0e8f15ce50_mtq6ntq6nte&buttonSessionID=uid_1e5135ffcf_mtq6ntq6nte&env=sandbox&buttonSize=huge&fundingEligibility=eyJwYXlwYWwiOnsiZWxpZ2libGUiOnRydWUsInZhdWx0YWJsZSI6dHJ1ZX0sInBheWxhdGVyIjp7ImVsaWdpYmxlIjpmYWxzZSwibWVyY2hhbnRDb25maWdIYXNoIjoiZmQxNWMxYTBkNTFiYjBlMTRjODkxYTUzNDYwZTZiYWU1MDkyZmEzZCIsInByb2R1Y3RzIjp7InBheUluMyI6eyJlbGlnaWJsZSI6ZmFsc2UsInZhcmlhbnQiOm51bGx9LCJwYXlJbjQiOnsiZWxpZ2libGUiOmZhbHNlLCJ2YXJpYW50IjpudWxsfSwicGF5bGF0ZXIiOnsiZWxpZ2libGUiOmZhbHNlLCJ2YXJpYW50IjpudWxsfX19LCJjYXJkIjp7ImVsaWdpYmxlIjp0cnVlLCJicmFuZGVkIjp0cnVlLCJpbnN0YWxsbWVudHMiOmZhbHNlLCJ2ZW5kb3JzIjp7InZpc2EiOnsiZWxpZ2libGUiOnRydWUsInZhdWx0YWJsZSI6dHJ1ZX0sIm1hc3RlcmNhcmQiOnsiZWxpZ2libGUiOnRydWUsInZhdWx0YWJsZSI6dHJ1ZX0sImFtZXgiOnsiZWxpZ2libGUiOnRydWUsInZhdWx0YWJsZSI6dHJ1ZX0sImRpc2NvdmVyIjp7ImVsaWdpYmxlIjpmYWxzZSwidmF1bHRhYmxlIjp0cnVlfSwiaGlwZXIiOnsiZWxpZ2libGUiOmZhbHNlLCJ2YXVsdGFibGUiOmZhbHNlfSwiZWxvIjp7ImVsaWdpYmxlIjpmYWxzZSwidmF1bHRhYmxlIjp0cnVlfSwiamNiIjp7ImVsaWdpYmxlIjpmYWxzZSwidmF1bHRhYmxlIjp0cnVlfX0sImd1ZXN0RW5hYmxlZCI6ZmFsc2V9LCJ2ZW5tbyI6eyJlbGlnaWJsZSI6ZmFsc2V9LCJpdGF1Ijp7ImVsaWdpYmxlIjpmYWxzZX0sImNyZWRpdCI6eyJlbGlnaWJsZSI6ZmFsc2V9LCJhcHBsZXBheSI6eyJlbGlnaWJsZSI6ZmFsc2V9LCJzZXBhIjp7ImVsaWdpYmxlIjpmYWxzZX0sImlkZWFsIjp7ImVsaWdpYmxlIjpmYWxzZX0sImJhbmNvbnRhY3QiOnsiZWxpZ2libGUiOmZhbHNlfSwiZ2lyb3BheSI6eyJlbGlnaWJsZSI6ZmFsc2V9LCJlcHMiOnsiZWxpZ2libGUiOmZhbHNlfSwic29mb3J0Ijp7ImVsaWdpYmxlIjpmYWxzZX0sIm15YmFuayI6eyJlbGlnaWJsZSI6ZmFsc2V9LCJwMjQiOnsiZWxpZ2libGUiOmZhbHNlfSwiemltcGxlciI6eyJlbGlnaWJsZSI6ZmFsc2V9LCJ3ZWNoYXRwYXkiOnsiZWxpZ2libGUiOmZhbHNlfSwicGF5dSI6eyJlbGlnaWJsZSI6ZmFsc2V9LCJibGlrIjp7ImVsaWdpYmxlIjpmYWxzZX0sInRydXN0bHkiOnsiZWxpZ2libGUiOmZhbHNlfSwib3h4byI6eyJlbGlnaWJsZSI6ZmFsc2V9LCJtYXhpbWEiOnsiZWxpZ2libGUiOmZhbHNlfSwiYm9sZXRvIjp7ImVsaWdpYmxlIjpmYWxzZX0sIm1lcmNhZG9wYWdvIjp7ImVsaWdpYmxlIjpmYWxzZX19&platform=desktop&experiment.enableVenmo=false&experiment.disablePaylater=false&experiment.enableVenmoAppLabel=false&flow=purchase&currency=EUR&intent=capture&commit=true&vault=false&renderedButtons.0=paypal&renderedButtons.1=card&debug=false&applePaySupport=false&supportsPopups=true&supportedNativeBrowser=false&allowBillingPayments=true:1338:178001
I tried putting only the amount into the data and it still doesn't work, I also tried to add an order ID but it doesn't work
This is a bit of a mess, you're doing an ajax call to a "create.php" on your server, but for some reason not calling the PayPal API from there (with the Checkout-PHP-SDK or similar), but are instead using that response to then create an order from the client-side (which is what actions.order.create does)
This doesn't make any real sense. If you are using a server, you should have two routes: create.php and capture.php?id=XXXXXXXXXXXXXXX" (use a JSON body parameter to fetch rather than GET URL string, if desired) both of which communicate with the PayPal API themselves, to create and capture an order respectively. It does not make sense to combine your own server operations with JS SDK client-side methods.
So, you should switch over completely to this server pattern example: https://developer.paypal.com/demo/checkout/#/pattern/server , and in each of the respective routes that are called (create.php / capture.php or similar) you can either write your own HTTPS calls for the PayPal API, or use the Checkout-PHP-SDK to abstract them as desired, and return the JSON response to the fetch caller.
I do not recommend trying to get what you have to work, but the main reason it is not working is a failure to return a JavaScript Promise properly. You are invoking an asynchronous $.ajax and not returning anything after doing so, hence the error "Expected an order id to be passed" since you actually returned nothing (even though you think you did); in reality you just spun off an asynchronous $.ajax to do its own thing and that function's result is never fed back (nor would it be if you returned its result, since it uses a callback rather than promises).
The simplest way to get a promise when calling your own server routes is to use fetch instead of $.ajax, then you can just return the result of the fetch. It is theoretically possible to turn a $.ajax callback into a promise and return that, but I would not spend any time on this if I were you; the built-in browser fetch creates a promise for you and is preferred in modern JS.

WebAuthn: Can't create public key. Promise is rejected

I am trying to get WebAuthn set up on our login page. I am to the part where I need to make the public key using navigator.credentials.create(). On Chrome, I keep getting the following error: Uncaught (in promise) DOMException: The operation either timed out or was not allowed. See: https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client.
Here is the relevant code:
if (isAvailable) {
// Get challenge from server
fetch("WebAuthn/WebAuthn.ashx", {
method: "POST"
})
.then(res => res.json())
.then(res => {
const publicKeyCredentialCreationOptions = {
challenge: Uint8Array.from(
res.data, c => c.charCodeAt(0)),
rp: {
id: "localhost",
name: "Company Name"
},
authenticatorSelection: {
authenticatorAttachment: "platform",
userVerification: "discouraged"
},
pubKeyCredParams: [{alg: -7, type: "public-key"}],
user: {
id: Uint8Array.from(
"UZSL85T9AFC", c => c.charCodeAt(0)),
displayName: "User",
name: document.getElementById("tbUser").value // taken from aspx form
}
};
const credential = navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
});
}
Some additional information that may be useful:
The server does not yet handle this information, but I don't think that matters since the credentials need to be created before they can be sent
Currently testing on https://localhost
This is taking place on the login page before the user is logged in. The idea is to prompt the user once they hit submit
Firstly, you can test out using virtual authenticator on Chrome, see image below.
On windows, you can setup Windows Hello as authenticator and test that later.
Now some notes for your problem
localhost does not need https
the expected origin specified if using only http, can be http://localhost<:port if not 80>
Need to check the format sent, is it arraybyte or not
I have managed to get it working... You can try and look at my example code and use them, only 2 files
Frontend: https://github.com/ais-one/cookbook/blob/develop/js-node/expressjs/public/demo-express/fido.html
Express Backend (using fido2-lib): https://github.com/ais-one/cookbook/blob/develop/js-node/expressjs/router/fido.js
I am still cleaning it up to make it to production code (e.g. using JWTs / Sessions when passing info between front and back end.
If you still have problems, we can discuss here... https://github.com/ais-one/cookbook/discussions
In my case, my supported public key algorithms list was too narrow to support platform authenticator attachment. Now I use this list of algs, and it's working as it should.
{"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -8
},
{
"type": "public-key",
"alg": -36
},
{
"type": "public-key",
"alg": -37
},
{
"type": "public-key",
"alg": -38
},
{
"type": "public-key",
"alg": -39
},
{
"type": "public-key",
"alg": -257
},
{
"type": "public-key",
"alg": -258
},
{
"type": "public-key",
"alg": -259
}]}

Paypal Express Checkout: Recurring payments on specific date

I am attempting to setup our website to accept recurring payments on a specific date (£5 on 1st June every year) regardless of when the user initially signs up (except for the month of May, then thier first rebill will be on 1st June the following year)
My searches so far have yielded that Express Checkout (checkout.js) is probably my best option.
I have managed to, in sandbox mode, take the initial payment of £5, but can't find any documentation on how to setup the recurring payment.
Code below
paypal.Button.render(
{
env: "sandbox", // 'production' Or 'sandbox',
// Pass the client ids to use to create your transaction on sandbox and production environments
client: {
sandbox:
[REDACTED], // from https://developer.paypal.com/developer/applications/
production:
[REDACTED] // from https://developer.paypal.com/developer/applications/
},
// Pass the payment details for your transaction
// See https://developer.paypal.com/docs/api/payments/#payment_create for the expected json parameters
payment: function(data, actions) {
return actions.payment.create({
transactions: [
{
amount: {
total: "5.00",
currency: "GBP"
}
}
]
});
},
// Display a "Pay Now" button rather than a "Continue" button
commit: true,
// Pass a function to be called when the customer completes the payment
onAuthorize: function(data, actions) {
console.log(data);
console.log(actions);
return actions.payment.execute().then(function(response) {
console.log("The payment was completed!");
});
},
// Pass a function to be called when the customer cancels the payment
onCancel: function(data) {
console.log("The payment was cancelled!");
}
},
"#paypal-button"
);

Creating an envelope from a template returning "UNSPECIFIED_ERROR"

When I try to create an envelope from a template I get a response of:
{ errorCode: 'UNSPECIFIED_ERROR',
message: 'Non-static method requires a target.' }
Here's what I'm doing so far:
First I login, which returns
{ loginAccounts:
[ { name: '*****',
accountId: '*****',
baseUrl: 'https://demo.docusign.net/restapi/v2/accounts/******',
isDefault: 'true',
userName: '***** ********',
userId: '*******-*****-*****-*****-*********',
email: '********#*******.com',
siteDescription: '' } ] }
So then I take the baseUrl out of that response and I attempt to create the envelope. I'm using the hapi framework and async.waterfall of the async library, so for anyone unfamiliar with either of these my use of the async library uses the next callback to call the next function which in this case would be to get the url for the iframe, and with our usage of the hapi framework AppServer.Wreck is roughy equivalent to request:
function prepareEnvelope(baseUrl, next) {
var createEntitlementTemplateId = "99C44F50-2C97-4074-896B-2454969CAEF7";
var getEnvelopeUrl = baseUrl + "/envelopes";
var options = {
headers: {
"X-DocuSign-Authentication": JSON.stringify(authHeader),
"Content-Type": "application/json",
"Accept": "application/json",
"Content-Disposition": "form-data"
},
body : JSON.stringify({
status: "sent",
emailSubject: "Test email subject",
emailBlurb: "My email blurb",
templateId: createEntitlementTemplateId,
templateRoles: [
{
email: "anemailaddress#gmail.com",
name: "Recipient Name",
roleName: "Signer1",
clientUserId: "1099", // TODO: replace with the user's id
tabs : {
textTabs : [
{
tabLabel : "acct_nmbr",
value : "123456"
},
{
tabLabel : "hm_phn_nmbr",
value : "8005882300"
},
{
tabLabel : "nm",
value : "Mr Foo Bar"
}
]
}
}
]
})
};
console.log("--------> options: ", options); // REMOVE THIS ====
AppServer.Wreck.post(getEnvelopeUrl, options, function(err, res, body) {
console.log("Request Envelope Result: \r\n", JSON.parse(body));
next(null, body, baseUrl);
});
}
And what I get back is:
{ errorCode: 'UNSPECIFIED_ERROR',
message: 'Non-static method requires a target.' }
From a little googling it look like 'Non-static method requires a target.' is a C# error and doesn't really give me much indication of what part of my configuration object is wrong.
I've tried a simpler version of this call stripping out all of the tabs and clientUserId and I get the same response.
I created my template on the Docusign website and I haven't ruled out that something is set up incorrectly there. I created a template, confirmed that Docusign noticed the named form fields, and created a 'placeholder' templateRole.
Here's the templateRole placeholder:
Here's one of the named fields that I want to populate and corresponding data label:
As a side note, I was able to get the basic vanilla example working without named fields nor using a template using the docusign node package just fine but I didn't see any way to use tabs with named form fields with the library and decided that I'd rather have more fine-grained control over what I'm doing anyway and so I opted for just hitting the APIs.
Surprisingly when I search SO for the errorCode and message I'm getting I could only find one post without a resolution :/
Of course any help will be greatly appreciated. Please don't hesitate to let me know if you need any additional information.
Once I received feedback from Docusign that my api call had an empty body it didn't take but a couple minutes for me to realize that the issue was my options object containing a body property rather than a payload property, as is done in the hapi framework.

Error every time I run datastore.runQuery: one of fields Query.query and Query.gql_query must be set

I'm trying to run a simple query against my Google Cloud datastore using google-api-nodejs-client. I want to query for all entities matching a given kind. When I run this query using the "Try it now" tool it works fine:
Request
POST https://www.googleapis.com/datastore/v1beta2/datasets/healthier-staging/runQuery?key={YOUR_API_KEY}
{
"query": {
"kinds": [
{
"name": "Subscriber"
}
]
}
}
Response
200 OK
{
"batch": {
"entityResultType": "FULL",
"entityResults": [
{
"entity": {
"key": {
"partitionId": {
"datasetId": "s~healthier-staging"
},
"path": [
{
"kind": "Subscriber",
"name": "+1215XXXXXXX"
}
]
},
"properties": {
...
I'm able to authenticate using my credentials, create a transaction, etc. so I know it's not an authentication issue.
Here's the code I'm trying to run in Node:
this.datastore.runQuery({
datasetId: 'healthier-staging',
query: {
kinds: [{name: 'Subscriber'}]
},
}, (function(err, result) {
if (err) {
console.error(err);
return;
}
}).bind(this));
When I try to run the same query using the Node module, I get this error:
{ [Error: one of fields Query.query and Query.gql_query must be set]
code: 400,
errors:
[ { domain: 'global',
reason: 'INVALID_ARGUMENT',
message: 'one of fields Query.query and Query.gql_query must be set' } ] }
This doesn't make sense, since I've specified the query field. I've tried all sorts of things: removing datasetId (produces an error about needing datasetId), using gql_query instead (same error), encapsulating the datasetId inside a transaction and passing that along inside readOptions, etc.
Is this a bug or am I doing something silly?
Thanks!
I mentioned this on your other StackOverflow question, but your request should be included in the resource section:
this.datastore.runQuery({
datasetId: 'healthier-staging',
resource: {
query: {
kinds: [{name: 'Subscriber'}]
},
},
}, (function(err, result) {
if (err) {
console.error(err);
return;
}
}).bind(this));

Categories

Resources