Google Checkout API - Connect order with notification? - e-commerce

Using the Google Checkout XML API, is it possible to connect an order you send to Google Checkout to an order Google Checkout notifies you about?
Right now, I can send an order to Google Checkout, and I can get notifications from Google that an order went through. But I don't see how to connect the two. I need this to connect orders to user accounts.

Send over an explicit order number with your data to Google. This is some test code I wrote that does just this
CheckoutShoppingCartRequest Req = checkoutButton.CreateRequest();
Req.AddItem(product.Name, product.ShortDescription, OrderID, product.Price, 1);
GCheckoutResponse Resp = Req.Send();
if (Resp.IsGood)
{
}
else
{
}

Generally, you can use the merchant-private-data field to pass any custom data about your order.
When you get back the notification, read the custom info from the merchant-private-data field.
See this answer for a solution to a similar issue.

Related

How to push Salesforce Order to an external REST API?

I have experience in Salesforce administration, but not in Salesforce development.
My task is to push a Order in Salesforce to an external REST API, if the order is in the custom status "Processing" and the Order Start Date (EffectiveDate) is in 10 days.
The order will be than processed in the down-stream system.
If the order was successfully pushed to the REST API the status should be changed to "Activated".
Can anybody give me some example code to get started?
There's very cool guide for picking right mechanism, I've been studying from this PDF for one of SF certifications: https://developer.salesforce.com/docs/atlas.en-us.integration_patterns_and_practices.meta/integration_patterns_and_practices/integ_pat_intro_overview.htm
A lot depends on whether the endpoint is accessible from Salesforce (if it isn't - you might have to pull data instead of pushing), what authentication it needs.
For push out of Salesforce you could use
Outbound Message - it'd be an XML document sent when (time-based in your case?) workflow fires, not REST but it's just clicks, no code. The downside is that it's just 1 object in message. So you can send Order header but no line items.
External Service would be code-free and you could build a flow with it.
You could always push data with Apex code (something like this). We'd split the solution into 2 bits.
The part that gets actual work done: At high level you'd write function that takes list of Order ids as parameter, queries them, calls req.setBody(JSON.serialize([SELECT Id, OrderNumber FROM Order WHERE Id IN :ids]));... If the API needs some special authentication - you'd look into "Named Credentials". Hard to say what you'll need without knowing more about your target.
And the part that would call this Apex when the time comes. Could be more code (a nightly scheduled job that makes these callouts 1 minute after midnight?) https://salesforce.stackexchange.com/questions/226403/how-to-schedule-an-apex-batch-with-callout
Could be a flow / process builder (again, you probably want time-based flows) that calls this piece of Apex. The "worker" code would have to "implement interface" (a fancy way of saying that the code promises there will be function "suchAndSuchName" that takes "suchAndSuch" parameters). Check Process.Plugin out.
For pulling data... well, target application could login to SF (SOAP, REST) and query the table of orders once a day. Lots of integration tools have Salesforce plugins, do you already use Azure Data Factory? Informatica? BizTalk? Mulesoft?
There's also something called "long polling" where client app subscribes to notifications and SF pushes info to them. You might have heard about CometD? In SF-speak read up about Platform Events, Streaming API, Change Data Capture (although that last one fires on change and sends only the changed fields, not great for pushing a complete order + line items). You can send platform events from flows too.
So... don't dive straight to coding the solution. Plan a bit, the maintenance will be easier. This is untested, written in Notepad, I don't have org with orders handy... But in theory you should be able to schedule it to run at 1 AM for example. Or from dev console you can trigger it with Database.executeBatch(new OrderSyncBatch(), 1);
public class OrderSyncBatch implements Database.Batchable, Database.AllowsCallouts {
public Database.QueryLocator start(Database.BatchableContext bc) {
Date cutoff = System.today().addDays(10);
return Database.getQueryLocator([SELECT Id, Name, Account.Name, GrandTotalAmount, OrderNumber, OrderReferenceNumber,
(SELECT Id, UnitPrice, Quantity, OrderId FROM OrderItems)
FROM Order
WHERE Status = 'Processing' AND EffectiveDate = :cutoff]);
}
public void execute(Database.BatchableContext bc, List<sObject> scope) {
Http h = new Http();
List<Order> toUpdate = new List<Order>();
// Assuming you want 1 order at a time, not a list of orders?
for (Order o : (List<Order>)scope) {
HttpRequest req = new HttpRequest();
HttpResponse res;
req.setEndpoint('https://example.com'); // your API endpoint here, or maybe something that starts with "callout:" if you'd be using Named Credentials
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(JSON.serializePretty(o));
res = h.send(req);
if (res.getStatusCode() == 200) {
o.Status = 'Activated';
toUpdate.add(o);
}
else {
// Error handling? Maybe just debug it, maybe make a Task for the user or look into
// Database.RaisesPlatformEvents
System.debug(res);
}
}
update toUpdate;
}
public void finish(Database.BatchableContext bc) {}
public void execute(SchedulableContext sc){
Database.executeBatch(new OrderSyncBatch(), Limits.getLimitCallouts()); // there's limit of 10 callouts per single transaction
// and by default batches process 200 records at a time so we want smaller chunks
// https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_limits.htm
// You might want to tweak the parameter even down to 1 order at a time if processing takes a while at the other end.
}
}

How do I get sorted results from the Google Photos search API?

I'm using the search API for Google Photos documented here. I'd like the results in the response to be sorted from newest to oldest, but by default, the results are sorted from oldest to newest. Is there a way to reverse the sorting?
I believe your goal as follows.
You want to sort the result values from the method of "Method: mediaItems.search".
You want to sort the values from oldest to newest.
Issue and workaround:
Unfortunately, in the current stage, it seems that there is no parameter for sorting the returned values for the the method of "Method: mediaItems.search" in Google Photos API. Also, it seems that such parameter is not existing in the method of "mediaItems.list".
By the way, it was found that when albumId is used in the request body for the method of "Method: mediaItems.search", the returned values are sorted as the ascending order. If you use the albumn ID, I think that your goal can be achieve by this.
On the other hand, when albumId is NOT used in the request body, the returned values are sorted as the descending order. And also, it seems that when filteres is used in the request body, the returned values are sorted as the descending order.
From your question, I thought that in your situation, albumId might be not used. So in this case, as the current workaround, how about sorting the values using a script after the values are retrieved? In this answer, I would like to propose to use the Web Apps created by Google Apps Script as a wrapper API.
Usage:
1. Create new project of Google Apps Script.
Sample script of Web Apps is a Google Apps Script. So please create a project of Google Apps Script.
If you want to directly create it, please access to https://script.new/. In this case, if you are not logged in Google, the log in screen is opened. So please log in to Google. By this, the script editor of Google Apps Script is opened.
2. Linking Cloud Platform Project to Google Apps Script Project.
About this, you can see the detail flow at here.
And also, please enable Google Photos API at API console.
3. Add scope.
In this case, please addt the scope of https://www.googleapis.com/auth/photoslibrary to the manifest file (appsscript.json).
4. Script.
Please copy and paste the following script (Google Apps Script) to the script editor. This script is for the Web Apps. This Web Apps is used as an API.
function doGet(e) {
const key = "sampleKey"; // This is used for using this Web Apps.
try {
if (e.parameter.key != key) throw new Error("Invalid key.");
const albumId = e.parameter.albumId;
const filters = e.parameter.filters;
const sort = e.parameter.sort;
const headers = {"Authorization": "Bearer " + ScriptApp.getOAuthToken()};
const url = "https://photoslibrary.googleapis.com/v1/mediaItems:search";
let mediaItems = [];
let pageToken = "";
const metadata = {pageSize: 100, pageToken: pageToken};
if (albumId) metadata.albumId = albumId;
if (filters) metadata.filters = JSON.parse(filters);
do {
const params = {
method: "post",
headers: headers,
contentType: "application/json",
payload: JSON.stringify(metadata),
}
const res = UrlFetchApp.fetch(url, params);
const obj = JSON.parse(res.getContentText());
mediaItems = mediaItems.concat(obj.mediaItems);
pageToken = obj.nextPageToken || "";
} while (pageToken);
if (mediaItems.length > 0) {
if (sort && sort == "ascending") {
mediaItems.sort((a, b) => new Date(a.mediaMetadata.creationTime) < new Date(b.mediaMetadata.creationTime) ? -1 : 1);
}
return ContentService.createTextOutput(JSON.stringify({values: mediaItems}));
}
return ContentService.createTextOutput(JSON.stringify({error: "No values."}));
} catch(err) {
return ContentService.createTextOutput(JSON.stringify({error: err.message}));
}
}
5. Deploy Web Apps.
The detail information can be seen at the official document.
On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
Please click "Select type" -> "Web App".
Please input the information about the Web App in the fields under "Deployment configuration".
Please select "Me" for "Execute as".
This is the important of this workaround.
Please select "Anyone" for "Who has access".
In this case, the user is not required to use the access token. So please use this as a test case.
When you want to use the access token, please set it to Anyone with Google account or Only myself. By this, the user can access to the Web Apps using the access token. When you use the access token, please include the scope of https://www.googleapis.com/auth/drive.readonly or https://www.googleapis.com/auth/drive.
Please click "Deploy" button.
When "The Web App requires you to authorize access to your data" is shown, please click "Authorize access".
Automatically open a dialog box of "Authorization required".
Select own account.
Click "Advanced" at "This app isn't verified".
Click "Go to ### project name ###(unsafe)"
Click "Allow" button.
Copy the URL of Web App. It's like https://script.google.com/macros/s/###/exec.
When you modified the Google Apps Script, please redeploy as new version. By this, the modified script is reflected to Web Apps. Please be careful this.
6. Testing.
As the test of this Web Apps, I would like to propose to use the following curl command. Please replace https://script.google.com/macros/s/###/exec with your Web Apps URL.
Simple use:
In this curl command, the result value is returned as the ascending order of oldest to newest.
$ curl -GL -d "key=sampleKey" -d "sort=ascending" https://script.google.com/macros/s/###/exec
Use albumId:
When you want to use the album ID, please use the following curl command.
$ curl -GL -d "albumId=###" -d "key=sampleKey" -d "sort=ascending" https://script.google.com/macros/s/###/exec
In this case, even when -d "sort=ascending" is not used, the result value is returned as the ascending order of oldest to newest.
Use filters:
When you want to use the filters, please use the following curl command.
$ curl -GL -d 'filters={"dateFilter":{"ranges":[{"startDate":{"year":2020},"endDate":{"year":2021}}]}}' -d "key=sampleKey" -d "sort=ascending" https://script.google.com/macros/s/###/exec
In this command, the values of 2020 - 2021 are returned as the ascending order of oldest to newest.
Note:
Although when I searched this at the Google issue tracker, I couldn't find about it. So how about reporting this as the future request? Ref
References:
Method: mediaItems.search
Related thread.
How to use Google Photos API Method: mediaItems.search in Google apps script for a spreadsheet
Google photos api adding photos not working, upload seems to work
Google Apps Scripts: import (upload) media from Google Drive to Google Photos?

How to receive encrypted payment token data of Google Pay

I'm trying to use Google Pay on my website. Once I confirm the payment Google returns this data structure:
But I need something like this:
{
"signature":"MEYCIQDTe92wpG6OUgxJ/8Qnr36XzSgjGGCM7R3LwxjgwPYMTAIhAJDrjHG9wEm1BxVM6MMMB+jGTGpi3VScEMVbHIUsObFi",
"protocolVersion":"ECv2",
"signedMessage":"{\"encryptedMessage\":\"FY8w/U3IbdsZQovX8ufNGFDOePgc/genRiMjHyvuIBqLY4a6uPz7wI0ra31K6YbFJlAnCjFhTwSvDxAYXw6hlmI8sESO5eM1eZlzfP3+NoKV80OXKvOM/xI/qOQEqpEQEVXx6Bw2EpMMFW8yBaA8XPUNee3EJlUk+/f8lRdRcNmI65QKPLAzUnySo75HzBkSc5It/8/oXdYwR93/K6HRKGZuD+bIaxy3SUvC9ehQqhBoP+A34yQX1knfJ1qjBMhhVVcPZHi+Bg6LXA4ms/lfDdim6D1Epr6XQhc2h4RZ/dT+6Enn81s/8ym+jMMs8kqsW9ib8vkdTARv9AbUu7zeGPrKTCAxwc1n6joRR72dSnNCI9j9sxd9tkuC9wuRyDmjbWT+hRZgLc1v/xzzNImo3NxdKdaPzKBE3t9XQZY5fp3lZELaoXAuxAZYtZ7bX64Mz9c28wD8EM+krvfbmGMiBjIt8EfeH48/SjeaUKfDu+yQnjPJAKbDZPhKJ1jqlY/ChP0Om7spQacT86QUVK/DwfzukwvwwRJkWydNEac5fgfS2T6ToZ+PW4VHbxkDnY/h+B0uwdlNQIL2a3Ar5Q\\u003d\\u003d\",\"ephemeralPublicKey\":\"BGkK4bSvob+e7ZgaNV/4vSJYYa10OJzd3aUk9yPEP6iNBRcfHiD/NTvhKjN4P24l0tEzH3L8TrySl6AczPJpCkw\\u003d\",\"tag\":\"xGEhEfJESIyBSfq2fExWiZxNWelnm3m4i8P7cgsarqg\\u003d\"}"
}
Could you please advice me what should I do to get the second data structure. Thank you!
The response that you are after is found under paymentMethodData.tokenizationData.token. However, the contents of this field are determined by the request parameters that you pass to loadPaymentData. Are you able to include the request that was used to generate the above response?
FYI, the following JSFiddle will produce a result similar to what you are after:
const tokenizationSpecification = {
  type: 'DIRECT',
  parameters: {
    'protocolVersion': 'ECv2',
'publicKey': 'BMzk6xvwPgU8vjB...7KRu4tuRmhm6nv8=',
  }
};
/*
{
"signature":"MEUCI...TougPg",
"protocolVersion":"ECv2",
"signedMessage": "{\"encryptedMessage\":\"VhQuaN...5o0Ny6Y\\u003d\"}"
}
*/
I achieved this by using a DIRECT tokenization specification which is discouraged:
Key Point: The Direct integration allows merchants to decrypt the Google Pay response on their servers. To qualify, you must be Payments Card Industry (PCI) Data Security Standard (DSS) Level 1 compliant. Your servers also need to have the required infrastructure to securely handle users' payment credentials.
Third parties that supply gateway or processing services on behalf of actual merchants aren't eligible to use the Direct integration. For questions about your integration as a payment service provider, contact us.
If you don't meet the necessary prerequisites, we recommend that you use a supported gateway to receive a payment token.
Not sure if this is still an open issue. But here are my 2 cents and might be the answer to your solution.
Please note: This is an answer if you are using WorldPay/Vantiv only.
From your first response above with the token, I believe you are using gateway as "vantiv"
"gateway": "vantiv"
"vantiv:merchantPayPageId": "YOUR_PAY_PAGE_ID"
"vantiv:merchantOrderId": "YOUR_ORDER_ID"
"vantiv:merchantTransactionId": "YOUR_TRANSACTION_ID"
"vantiv:merchantReportGroup": "*web"
If you want to get the second response with signature and signedMessage then all you have to do is change the gateway to "worldpay" and you should get the same response:
"gateway": "worldpay"
"gatewayMerchantId": "YOUR_WORLDPAY_MERCHANT_ID"
You can find the request here

How can i know that my Youtube API Data is correct?

I having some trouble to understand something related to the API of youtube
So my code is basically very simple:
name = input("enter the username: ")
key = "MY API KEY"
data = urllib.request.urlopen("https://www.googleapis.com/youtube/v3/channels?
part=statistics&forUsername="+name+"&key="+key).read()
subs = json.loads(data)["items"][0]["statistics"]["subscriberCount"]
print(name + " has " + "{:,d}".format(int(subs)) + " subscribers!🎉")
just yelling the number of subscribers after giving specific YouTube Username:
The thing is that some Usernames(for example: Vsuase/Veritasium/Unbox Therapy ) which have many subs and the API-URL giving me wrong Data
Vsause - in return giving me back 72 subs
Veritasium/Unbox Therapy - not giving my any number at all
BUT, a channel "Computerphile" giving me that exact same subs they have.
How come that few Usernames work and few do not??
I tested in both, using the try-it functionality available in the YouTube Data API - Official Documentation and in the Google API Explorer and in both sites the results are closely1 similar.
For example, when the statistics of the YouTube user vsauce is requested vía YouTube API, the value in subscriberCount is 14220819 and checking his YouTube channel it says: 14,220,825.
Here is the example for request the statistics of the YouTube user vsauce (using the try-it)
And here is the demo for request the statistics of the YouTube user vsauce (using the Google API Explorer).
I didn't see any differences in the values in subscriberCount by requesting the other channels you mentioned in your question.
1 You need consider that some channels has more changes in the quantity of subscribers than others and such results vary too in the responses of the API.
For some reason, if you change in the URL from forUsernae= --> id=
it gives you the correct numbers.
TED channel:
https://www.googleapis.com/youtube/v3/channels?part=statistics&id=UCAuUUnT6oDeKwE6v1NGQxug&key=AIzaSyDjnINqqxQlIg4kbXoPDVYOhHNfdmDbb9I

twitter stream API track all tweet posted by user who registered in twitter application

I am creating the application which need to track all tweets from user who registered to my application, i tried to track those with streaming API , there are public API, user API , and site API,
in those API it just have an option to follow the user ID by add the comma separated user ID
https://dev.twitter.com/streaming/overview/request-parameters#follow
but i think it is not flexible, if there are a new user registered , i need to rebuild the HTTP request , and also if there are so many users try to listen this stream and query will be so long,
it will be
https://stream.twitter.com/1.1/statuses/filter.json?follow=[user1],[user2],[user3]........[userN],
i afraid the query wont fit, i just need a parameter to filter all user who registered in my application such as, for example.
https://stream.twitter.com/1.1/statuses/filter.json?application=[applicationID]
but i think twitter dev does not provide it
so, is there any way to filter stream by application ID?
I didn't see anything like tracking by application id. If your query become too complex (too many follows/keywords), public streaming api will reject it,
and you can't open more than 2 connections with user stream. So, last solution is using Site Stream, -> you can open as many user connections as you have users registered to your app.
BUT the docs says :
"Site Streams is currently in a closed beta. Applications are no
longer being accepted."
Contact twitter to be sure
Arstechnica has a very interesting article about it. Take a look at this code and the link in the end of this post
If you are using python pycurl will do the job. Its provides a way to execute a function for every little piece of data received.
import pycurl, json
STREAM_URL = "http://chirpstream.twitter.com/2b/user.json"
USER = "YOUR_USERNAME"
PASS = "XXXXXXXXX"
userlist = ['user1',...,'userN']
def on_receive(self, data):
self.buffer += data
if data.endswith("rn") and self.buffer.strip():
content = json.loads(self.buffer)
self.buffer = ""
if "text" in content and content['user'] in userlist:
#do stuff
conn = pycurl.Curl()
conn.setopt(pycurl.USERPWD, "%s:%s" % (USER, PASS))
conn.setopt(pycurl.URL, STREAM_URL)
conn.setopt(pycurl.WRITEFUNCTION, on_receive)
conn.perform()
You can find more information here Real time twitter stream api