How to get the Units by day via API - App store connect - app-store-connect

Is it possible to get the data from Units download by day or installed via API ? but the problem is hard to find the resources of documentation of it.
this image below is the data I want to have.
https://i.stack.imgur.com/P3NDF.png

The only problem is that in this report generated by the API, the "Units" column counts the downloads, the "in-app purchases", "re-downloads" or other things, and this causes a difference in the number of units seen in the review on the Apple Connect Store, as #CameronPorter mentioned. However, when reading the documentation, I couldn't find a way to get only the downloads (units without the in-app purchase). See explanations below:
Explanation of the problem cited in the Apple documentation
Link for reference: https://help.apple.com/app-store-connect/#/dev5340bf481

It has several steps to archieve. First you have to follow 2 links here:
Create keys: https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api
Create and sign JWT Token
https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests
These important keys to get is:
IssuerId
KeyId
VendorId
PrivateKey
If you are using Python, I would suggest using PyJWT to sign it
from datetime import datetime, timezone
import jwt
def sign_appstore_token(issuer_id, key_id, generated_private_key):
bin_private_key = generated_private_key.encode()
current_unix = int(datetime.now(tz=timezone.utc).timestamp())
token = jwt.encode({
"iss": issuer_id,
"iat": current_unix,
"exp": current_unix + 1000,
"aud": "appstoreconnect-v1",
}, key= bin_private_key, algorithm= 'ES256', headers= {
"alg": "ES256",
"kid": key_id,
"typ": "JWT"
})
return token
From generated token, continue following this link
https://developer.apple.com/documentation/appstoreconnectapi/download_sales_and_trends_reports
To get the Units, reportType should be SALES. Also noticed that reportDate and frequency have to consistency each other, if you specify filter[frequency] = YEARLY, then filter[reportDate] = 2021 or filter[frequency] = MONTHLY, then filter[reportDate] = 2021-06. For more details, please refer to the above link
Sample query here:
https://api.appstoreconnect.apple.com/v1/salesReports?filter[frequency]=YEARLY&filter[reportDate]=2021&filter[reportSubType]=SUMMARY&filter[reportType]=SALES&filter[vendorNumber]=YOUR_VENDOR_ID
Headers: Authorization: Bearer YOUR_ABOVE_TOKEN
You will get binary response if it is success, represented for .gz file as well. Extract gz to get .txt schema deliminated by \t
Columns:
Provider Provider Country SKU Developer Title Version Product Type Identifier Units Developer Proceeds Begin Date End Date Customer Currency Country Code Currency of Proceeds Apple Identifier Customer Price Promo Code Parent Identifier Subscription Period Category CMB Device Supported Platforms Proceeds Reason Preserved Pricing Client Order Type
Python script here returns file content as text, you can do your next step, pandas table, or to model, it is up to you
import requests
import gzip
def download_appstore_objects(token, vendor_id, frequency, reportDate):
link = f'https://api.appstoreconnect.apple.com/v1/salesReports?filter[frequency]={frequency}&filter[reportDate]={reportDate}&filter[reportSubType]=SUMMARY&filter[reportType]=SALES&filter[vendorNumber]={vendor_id}'
response = requests.get(link, headers= {'Authorization': f'Bearer {token}' })
file_content = gzip.decompress(response.content).decode('utf-8')
return file_content

Related

Any POST or GET requests from the Revue API return 401

I am trying to add subscribers to my newsletter using the Revue api. According to the documentation, I need to add a header called 'Authorization' and value 'Token MY-TOKEN' in my requests.
In order to test out the API I am using Postman as seen in the screenshot below:
Any request I do to any url, ends up with a 401.
What am I missing here? The token value is copy pasted from the bottom of https://www.getrevue.co/app/integrations ('Your API key is xyz') as the documentation mentions. Double checked that there are no extra spaces added.
You cannot use the API (or at least certain entry points) without first verifying your account
If you fail to do this, your requests end with a 401 status.
Verify your account
There is no explicit option to verify your account. Instead, you need to import an existing mailing list. If you don't have a list to import, you can enforce this step by creating an artificial list, containing just your email. To do so, visit your Revue dashboard, Subscribers section, and click "Import from file":
.
Then, enter your email and two commas to skip your first and last name:
Submit and fill the next form.
When completed, a top ribbon indicates that the review is on its way:
You need to wait for the review to be completed.
Perform a request
To get your API key, visit your Revue dashboard, Account settings, Integrations, and scroll down to the bottom of the page:
Run something like:
const revueApiKey = "[your API key]";
const result = await fetch('https://www.getrevue.co/api/v2/subscribers', {
method: 'POST',
headers: {
Authorization: `Token ${revueApiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ email: "john#example.com", double_opt_in: false })
});
If you have the following when you log in to Revue
"We are reviewing your account."
You will not be able to make API calls and will get a 401.
I've talked to support on the issue and unfortunately, it's undocumented at the moment.
Took nearly a week for me to get reviewed but it's working fine now. It is at the end of the Christmas period so I am hoping they are only temporarily that slow at reviewing accounts.

Are there examples of itineraries that are compatible with the Amadeus self-service trip parse API?

I've tried booking references from a dozen providers (which I don't want to post for privacy reasons) and every time the API returns 'Unable to parse' but with no additional diagnostic information.
As a self-service API they don't offer support through any channel other than Stack Overflow, but I'm hoping someone has successfully used the endpoint.
I'm mostly using GMail to access sample flight booking emails, then selecting "View Original" to download the original MIME format email
This is what I use to read the .eml file into code:
function base64_encode(file) {
// read binary data
var bitmap = fs.readFileSync(file);
// convert binary data to base64 encoded string
return new Buffer(bitmap).toString('base64');
}
However every single email I submit to the endpoint eventually returns:
data:
{ data:
{ type: 'trip-parser-job',
id: 'REDACTED',
self: [Object],
status: 'ERROR',
detail: 'Unable to parse' } } }
and at this point, I'm starting to think that either the API is broken, or they haven't correctly documented what data should be submitted as content. I've decoded the sample document they provide and can't see any major difference between that and my inputs.
Does someone have either some working samples that the API was able to process, or some NodeJS code which seems to reliably get a result from the API?

eBay Browse API: shopping cart add item doesn't work

Working with Ebay's Browse API I'm having an issue with the /shopping_cart/add_item method, when I call it, it systematically returns an error 204.
I'm working on the API's sandbox. The calls are made from an iOS application in Canada. Until then, I had no issues requesting and retrieving data from responses.
Here's how I proceed:
Using the sandbox, I retrieve mock items with /item_summary/search.
The user goes through the OAuth process and grants his shopping cart access to my app. It returns a user access token that I use for the following request.
Finally, I call add_item with the following parameters:
Request JSON parameters:
{"quantity": 1, "itemId": "v1|110385018358|0"}
Request headers:
Authorization: Bearer [sandbox user access token from step 2.]
Content-Type: application/json
X-EBAY-C-MARKETPLACE-ID: EBAY_US
Then I get an empty response (aka error 204), while it shouldn't as mentioned in the documentation.
The response I get only contains headers which are the following:
{
Status Code: 204, Headers {
Connection = ( "keep-alive" );
"Content-Encoding" = ( gzip );
"Content-Length" = ( 0 );
"Content-Type" = ( "application/json" );
Date = ( "Thu, 22 Nov 2018 15:14:32 GMT" );
RlogId = ( "t6q%60ktkjvdbwrfsl%2Bbmsgcufboja%7Ct6n%3C%3Dsm%7Eufhuoluefqqgwj%284%3F34%3F11%2Busqdrrp%2Bufmadh%7B%2Bceb%7Ce4-fij-1673bfca0ca-0x133" );
"Set-Cookie" = ( "dp1=bu1p/QEBfX0BAX19AQA**5dd7fb58^;Domain=.ebay.com;Expires=Sat, 21-Nov-2020 15:14:32 GMT;Path=/" );
"X-EBAY-C-REQUEST-ID" = ( "ri=LVOZVdAO%2FSpS,rci=n76DxeaOd61P0WBf" );
"X-EBAY-C-VERSION" = ( "1.0.0" );
"X-EBAY-REQUEST-ID" = ( "1673bfca0a9.a0962ac.25e7e.fffdc702!/buy/browse/v1/shopping_cart!10.9.98.172!esbnewesbngcos[]!add_item!10.9.103.137!r1remshopcartapi-envadvcdhidzs5k[ItemClient[!Ginger.ViewItemServiceV1.litedetails!10.9.99.212!r1viappsvc-envadvcdhidzs5k[]]!ShopcartServiceClient[!Ginger.shopcase.v2.POST!10.9.101.40!r1scartsvc-envadvcdhidzs5k[]]]" );
"X-EBAY-SVC-EP-COOKIELET" = ( "321=0001542899671242" );
"X-EBAY-SVC-TRACKING-DATA" = ( "<a>nqt=AA**&!_epec=7,6,8&nqc=AA**</a>" );
}
}
Moreover, logged in the ebay sandbox website with the test user I used above, if I open the cart, I get an error page stating:
We were unable to load your cart. Please try again. If the problem persists, contact Customer Support or send us feedback.
Still from the sandbox website, when I open any item, I get another error stating:
Unfortunately, access to this particular item has been blocked due to legal restrictions in some countries. [...]
I still hope the problem is on me and not on Ebay as their API is still a Beta.
Well ... It was nowhere in the API documentation, but hidden somewhere on the Sandbox website page "unsupported feature list for the sandbox":
Cart is not supported. You may see some functionality working, like adding items to your cart, but please do not depend on or expect cart to function properly.
Although it states that AddItem may work, it actually does not, so I guess it is expected and that I should take this as the answer to my question.
Quite frustrating...
I guess I all I can do is to wait for Ebay's partnership approval.

Can I set variable monthly payment amounts through PayPal REST API's billing plan/agreement? On existing agreements?

I entered this as an issue on the PayPal-PHP-SDK github but it's rather time sensitive at this point.
Our goal is a subscription service that monthly charges users a value they select for each content update during that month. A $1 subscriber would pay $8 if there were 8 updates, for example.
Our implementation so far (which is already live with a number of subscribers) is using the PayPal-PHP-SDK / REST API to create a Billing Plan for each subscriber with a payment definition set to an amount calculated to be the maximum potential monthly charge. I then expected to be able to use Agreement->setBalance() to lower the value to the actual intended charge and then Agreement->billBalance() to process the payment.
Unfortunately due to time pressures we launched without verifying this was a viable implementation, and I've discovered that those functions are only for unpaid/delinquent balances. The start_date on our plans is April 1, and we'll have had 4 content releases but we're set to charge our subscribers for the maximum possible 9 updates.
I've tried a variety of Agreement->update() and Plan->update() calls to change the monthly value, along the lines of:
$Patch = new PayPal\Api\Patch();
$Patch->setOp("replace")
->setPath("/payment_definitions/0/amount/value")
->setValue($patch_value);
$PatchRequest = new PayPal\Api\PatchRequest();
$PatchRequest->addPatch($Patch);
$Plan = PayPal\Api\Plan::get($plan_id, $apiContext);
$Plan->update($PatchRequest, $apiContext);
which returns an exception with "validation_error: Invalid Path provided". Attempts to json encode the data path, up to and including the entire Plan object with $Patch->setPath("/") instead give an exception with "MALFORMED_REQUEST - Incoming JSON request does not map to API request". I suspect most values cannot be updated on an executed agreement or activated plan.
However, I see through the merchant account's website that the monthly value can be manually updated, so I hold out hope that I'm simply going about my API requests the wrong way.
I've also tried creating a Payment object with Transaction->setPurchaseUnitReferenceId($AgreementId) since I'd read something about reference transactions being a potential solution, but I'm getting the same malformed request error:
$Item = new PayPal\Api\Item();
$Item->setCategory('DIGITAL')
->setPrice($pledge_value)
->setDescription($update_name);
$ItemList = new PayPal\Api\ItemList();
$ItemList->addItem($Item);
$Amount = new PayPal\Api\Amount();
$Amount->setCurrency('USD')
->setTotal($pledge_value);
$Transaction = new PayPal\Api\Transaction();
$Transaction->setPurchaseUnitReferenceId($AgreementId)
->setDescription($update_name)
->setAmount($Amount)
->setItemList($ItemList)
->setNotifyUrl($notify_url);
$Payer = new PayPal\Api\Payer();
$Payer->setPaymentMethod('paypal');
$RedirectUrls = new PayPal\Api\RedirectUrls();
$RedirectUrls->setReturnUrl($return_url)
->setCancelUrl($cancel_url);
$Payment = new PayPal\Api\Payment();
$Payment->setIntent('sale')
->setPayer($Payer)
->setRedirectUrls($RedirectUrls)
->addTransaction($Transaction);
$Payment->create($apiContext);
So is there a way to fix our current implementation and plans/agreements? Failing that, is there a REST API solution that I should have used / can try to migrate to? I'd like to avoid Classic API if possible.

JIRA - Send email to anonymous user

I'm trying to figure out a way to send an email to an anonymous user when one creates an issue through the email. What I need is for this anonymous user to receive a notification email when the issue was opened,commented and closed.
According to their official documentation this can only be done if the creator is already a user in JIRA or if a user will be created on the fly. None of which works for me.
The work-arounds that I found so far are:
JEMH - which promises this functionality but looks unstable, meaning it seems to break (at least for a little bit) with every JIRA update and no downtime is acceptable for me.
Writing my own script as was recommended in the similar thread
I have no problems writing my own script but I just wanted to be sure I won't be reinventing the wheel. Are there any other ways of doing this?
I'll be very greatful for any help.
I just noticed this question. JEMH has now evolved into a fully fledged commercial plugin, and has a mass of new features, some of which actually address supporting remote 'anonymous' users for issue creation, essentially turning JIRA into a fully capable email helpdesk solution. Specific template customization is available for this on a per-event basis.
Regarding breakages, staying at the 'latest' release gives developers absolutely no time to catchup. Play smart, give all developers a chance to catchup.
With the depths of JIRA API's that JEMH plumbs, breakages were unfortunately common, but now are less likely thanks to Atlassian stabilizing some core API's in 5.0+. Work also also underway to provide end-end integration testing, which is a mission in its own right!
Here is how I did it using the Script Runner pluging, I've told Jira to get emails from my mailbox, and create issues from them. Than, on the workflow, I saved the sender's email and name to a custom fields using the following script:
from com.atlassian.jira import ComponentManager
import re
cfm = ComponentManager.getInstance().getCustomFieldManager()
# read issue description
description = issue.getDescription()
if (description is not None) and ('Created via e-mail received from' in description):
# extract email and name:
if ('<' in description) and ('>' in description):
# pattern [Created via e-mail received from: name <email#company.com>]
# split it to a list
description_list = re.split('<|>|:',description)
list_length = len(description_list)
for index in range(list_length-1, -1, -1):
if '#' in description_list[index]:
customer_email = description_list[index]
customer_name = description_list[index - 1]
break
else:
# pattern [Created via e-mail received from: email#company.com]
customer_name = "Sir or Madam"
# split it to a list
description_list = re.split(': |]',description)
list_length = len(description_list)
for index in range(list_length-1, -1, -1):
if '#' in description_list[index]:
customer_email = description_list[index]
break
# if the name isn't in the right form, switch it's places:
if (customer_name[0] == '"') and (customer_name[-1] == '"') and (',' in customer_name):
customer_name = customer_name[1:-1]
i = customer_name.index(',')
customer_name = customer_name[i+2:]+" "+customer_name[:i]
# insert data to issue fields
issue.setCustomFieldValue(cfm.getCustomFieldObject("customfield_10401"),customer_email)
issue.setCustomFieldValue(cfm.getCustomFieldObject("customfield_10108"),customer_name)
than, send the mail using the following script:
import smtplib,email
from smtplib import SMTP
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email import Encoders
import os
import re
from com.atlassian.jira import ComponentManager
customFieldManager = ComponentManager.getInstance().getCustomFieldManager()
cfm = ComponentManager.getInstance().getCustomFieldManager()
# read needed fields from the issue
key = issue.getKey()
#status = issue.getStatusObject().name
summary = issue.getSummary()
project = issue.getProjectObject().name
# read customer email address
toAddr = issue.getCustomFieldValue(cfm.getCustomFieldObject("customfield_10401"))
# send mail only if a valid email was entered
if (toAddr is not None) and (re.match('[A-Za-z0-9._%+-]+#(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,4}',toAddr)):
# read customer name
customerName = issue.getCustomFieldValue(cfm.getCustomFieldObject("customfield_10108"))
# read template from the disk
template_file = 'new_case.template'
f = open(template_file, 'r')
htmlBody = ""
for line in f:
line = line.replace('$$CUSTOMER_NAME',customerName)
line = line.replace('$$KEY',key)
line = line.replace('$$PROJECT',project)
line = line.replace('$$SUMMARY',summary)
htmlBody += line + '<BR>'
smtpserver = 'smtpserver.com'
to = [toAddr]
fromAddr = 'jira#email.com'
subject = "["+key+"] Thank You for Contacting Support team"
mail_user = 'jira#email.com'
mail_password = 'password'
# create html email
html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" '
html +='"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">'
html +='<body style="font-size:12px;font-family:Verdana">'
html +='<p align="center"><img src="http://path/to/company_logo.jpg" alt="logo"></p> '
html +='<p>'+htmlBody+'</p>'
html +='</body></html>'
emailMsg = email.MIMEMultipart.MIMEMultipart('alternative')
emailMsg['Subject'] = subject
emailMsg['From'] = fromAddr
emailMsg['To'] = ', '.join(to)
emailMsg.attach(email.mime.text.MIMEText(html,'html'))
# Send the email
s = SMTP(smtpserver) # ip or domain name of smtp server
s.login(mail_user, mail_password)
s.sendmail(fromAddr, [to], emailMsg.as_string())
s.quit()
# add sent mail to comments
cm = ComponentManager.getInstance().getCommentManager()
email_body = htmlBody.replace('<BR>','\n')
cm.create(issue,'anonymous','Email was sent to the customer ; Subject: '+subject+'\n'+email_body,False)
content of new_case.template:
Dear $$CUSTOMER_NAME,
Thank you for contacting support team.
We will address your case as soon as possible and respond with a solution very quickly.
Issue key $$KEY has been created as a reference for future correspondence.
If you need urgent support please refer to our Frequently Asked Questions page at http://www.example.com/faq.
Thank you,
Support Team
Issue key: $$KEY
Issue subject: $$PROJECT
Issue summary: $$SUMMARY
All scripts should be attach to the workflow, to Create transition.The scripts are written using Jython, so it needs to be installed to use it.
I doubt this functionality is available already built-in to JIRA, and I have not seen a plugin that will do it.
I looked into this in the past and came up empty. I suspect it is not built in because for many potential customers it would allow them to get away with a 10 user license and yet still support thousands of users.
We went with the unlimited user license instead.
Update: I meant to add to this that you could write a script that would do this, I think. But it looks like it would be a PITA with having to create a custom listener for it to capture changes to the issue https://developer.atlassian.com/display/DOCS/Plugin+Tutorial+-+Writing+event+listeners+with+the+atlassian-event+library
You can send notification to email stored in Jira custom field using Raley Email Notifications
The configuration is trivial, here's an example how to do it:
https://wiki.raleyapps.com/display/RAL/Sending+email+to+value+from+JIRA+issue+custom+field