├── README.md ├── c-sharp ├── export_example.cs ├── export_example.csproj └── export_example.sln └── python ├── create_export.py └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | # Merchant Data Export API Example (beta) 2 | 3 | ### This endpoint is currently in beta. It is subject to change without notice. 4 | 5 | ## Requirements 6 | 7 | * Python 3.7.3 (backwards compatible with 2.7) 8 | * pip 9 | * [Sandbox Clover developer account](https://sandbox.dev.clover.com/developers) 10 | 11 | ## Purpose 12 | 13 | This endpoint will allow you to get bulk payment and order data from merchants that have installed your app. This service is not real-time, but will significantly reduce your chances of hitting an API limit restriction. Developers that require order and payment histories on merchants should integrate this service into their data collecting implementation. 14 | 15 | 16 | ## Usage Notes 17 | 18 | - Availability of the service: 19 | 20 | - US (https://api.clover.com) UTC: MON-FRI 10:00-15:00, SAT 10:00-15:00, SUN 10:00-15:00 21 | - EU (https://api.eu.clover.com) UTC: MON-FRI 01:00-05:00, SAT 02:00-05:00, SUN 02:00-06:00 22 | - Sandbox (https://apisandbox.dev.clover.com) is not limited to certain times. 23 | 24 | Outside of these times, we return a 503 with available hours. 25 | 26 | 27 | 28 | - 1000 objects per file returned. If your response is more than 1000 objects we'll return an array of files to capture. 29 | 30 | 31 | - Each export file is available for 24hrs after which we'll delete it. 32 | 33 | 34 | - The max time range is 30 days. So if you want spans of data longer than that, you'll need to send a few requests. 35 | 36 | 37 | - The server can only handle a few concurrent exports at a time You will need to wait for each export to finish before starting another one. The server will throw 503s when the concurrency threshold is reached. 38 | 39 | 40 | 41 | ## How to use 42 | 43 | You will do the POST call to `/v3/merchants/{merchant_Id}/exports` with the appropriate payload. 44 | 45 | The payload includes: 46 | - `export_type` (PAYMENTS or ORDERS) 47 | - `start_time` (in UTC) 48 | - `end_time` (in UTC) 49 | ``` 50 | { 51 | "type": export_type, 52 | "startTime": start_time, 53 | "endTime": end_time 54 | } 55 | ``` 56 | 57 | 58 | The response of the exports object will include the export id for you to use. 59 | ``` 60 | { 61 | "id": "export_id", 62 | "type": "ORDERS", 63 | "status": "PENDING", 64 | "percentComplete": 0, 65 | "startTime": start_time, 66 | "endTime": end_time, 67 | "createdTime": created_time, 68 | "modifiedTime": modified_time, 69 | "merchantRef": { 70 | "id": "merchant_id" 71 | } 72 | } 73 | ``` 74 | 75 | 76 | Once the call has been made, you will occasionally do a GET call to `/v3/merchants/{merchant_Id}/exports/{export_Id}` to check the status and percent complete. 77 | 78 | 79 | STATUS: 80 | - `PENDING` - In queue to be processed 81 | - `IN_PROGRESS` - Being processed 82 | - `DONE` - Process complete 83 | 84 | 85 | ``` 86 | { 87 | "status": "PENDING", 88 | "modifiedTime": modified_time, 89 | "merchantRef": { 90 | "id": "merchant_id" 91 | }, 92 | "percentComplete": 0, 93 | "startTime": start_time, 94 | "createdTime": created_time, 95 | "endTime": end_time, 96 | "type": "ORDERS", 97 | "id": "export_id" 98 | } 99 | ``` 100 | 101 | 102 | Once it is complete your export object will include urls to the files for you to download the export. 103 | 104 | 105 | ``` 106 | { 107 | "id": "export_id", 108 | "type": "ORDERS", 109 | "status": "DONE", 110 | "percentComplete": 100, 111 | "availableUntil": available_until_time, 112 | "startTime": start_time, 113 | "endTime": end_time, 114 | "createdTime": created_time, 115 | "modifiedTime": modified_time, 116 | "exportUrls": { 117 | "elements": [ 118 | { 119 | "url": URL, 120 | "export": { 121 | "id": "export_id" 122 | } 123 | } 124 | ] 125 | }, 126 | "merchantRef": { 127 | "id": "merchant_id" 128 | } 129 | } 130 | ``` 131 | -------------------------------------------------------------------------------- /c-sharp/export_example.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Demonstrates basic usage of the Merchant Data Export API 3 | More information and additional examples can be found at https://github.com/clover/export-api-examples 4 | 5 | Script Usage 6 | ------------ 7 | Update these configuration variables defined below: 8 | - HOST: The target API host 9 | - MERCHANT_ID: The Clover ID of the merchant whose data you want to export 10 | - ACCESS_TOKEN: A valid API token with access to the target merchant 11 | - EXPORT_TYPE: The type of data to export('ORDERS' or 'PAYMNENTS') 12 | - START_TIME: The start(lower-bound) of the export time window 13 | - END_TIME: The end(upper-bound) of the export time window 14 | */ 15 | 16 | 17 | 18 | using System; 19 | using System.Threading; 20 | using System.Threading.Tasks; 21 | using System.Net.Http; 22 | using System.Net.Http.Headers; 23 | using System.Text; 24 | using Newtonsoft.Json.Linq; 25 | 26 | namespace WebAPIClient 27 | { 28 | class Program 29 | { 30 | 31 | // Begin Script Configuration 32 | public static String host = "https://apisandbox.dev.clover.com"; 33 | public static String merchant_id = ""; 34 | public static String api_token = ""; 35 | public static String export_type = "ORDERS"; // ORDERS or PAYMENTS 36 | public static long startTimeUTC = 1513065600000; //Dec 12, 2017 12:00 AM UTC 37 | public static long endTimeUTC = 1515657540000; // Jan 10, 2018 11:59 PM UTC 38 | // End Script Configuration 39 | 40 | 41 | public static String export_endpoint = $"{host}/v3/merchants/{merchant_id}/exports/"; 42 | public static HttpClient client = new HttpClient(); 43 | 44 | private static async Task BeginExport() 45 | { 46 | string json_request = $@"{{""type"": ""{export_type}"", ""startTime"": {startTimeUTC}, ""endTime"": {endTimeUTC}}}"; 47 | Console.WriteLine($"Export Endpoint: {export_endpoint}"); 48 | Console.WriteLine("POSTing payload: "); 49 | Console.WriteLine(JObject.Parse(json_request)); 50 | 51 | var response = await client.PostAsync(export_endpoint, new StringContent(json_request)); 52 | var msg = await response.Content.ReadAsStringAsync(); 53 | 54 | var current_status = JObject.Parse(msg)["status"]; 55 | // The response will include the current status of the export. Our export job is in the queue and waiting to be processed. 56 | if (current_status != null && current_status.ToString() == "PENDING") 57 | { 58 | var export_id = JObject.Parse(msg)["id"].ToString(); 59 | Console.WriteLine($"Export ID: {export_id}"); 60 | Console.WriteLine("Request sent. Export pending."); 61 | // And now we... 62 | await WaitForLink(export_id); 63 | } 64 | else{ 65 | Console.Write(msg); 66 | }; 67 | } 68 | 69 | private static async Task WaitForLink(String export_id) 70 | { 71 | // Now we'll keep an eye on /v3/merchants/{merchant_id}/exports/{export_id} so we know when our export is ready 72 | var response = await client.GetAsync(export_endpoint + export_id);; 73 | var msg = await response.Content.ReadAsStringAsync(); 74 | var current_status = JObject.Parse(msg)["status"].ToString(); 75 | 76 | // PENDING: Process hasn't been started yet 77 | // IN_PROGRESS: Exporting is in progress 78 | while (current_status == "PENDING" || current_status == "IN_PROGRESS") 79 | { 80 | Console.WriteLine($"Status: {current_status}"); 81 | // Wait for about 5 seconds before checking the endpoint again 82 | Thread.Sleep(5000); 83 | 84 | response = await client.GetAsync(export_endpoint + export_id);; 85 | msg = await response.Content.ReadAsStringAsync(); 86 | current_status = JObject.Parse(msg)["status"].ToString(); 87 | } 88 | 89 | // And we're done! A list of URLs to obtain your exported information. 90 | Console.WriteLine("=========Export URLs========="); 91 | Console.WriteLine(JObject.Parse(msg)["exportUrls"]["elements"].ToString()); 92 | } 93 | 94 | static void Main(string[] args) 95 | { 96 | client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", api_token); 97 | BeginExport().Wait(); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /c-sharp/export_example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp2.0 10 | Clover Example 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /c-sharp/export_example.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2017 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "export_example", "export_example.csproj", "{54382917-A916-4D9C-A4C9-5441F51230F3}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {54382917-A916-4D9C-A4C9-5441F51230F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {54382917-A916-4D9C-A4C9-5441F51230F3}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {54382917-A916-4D9C-A4C9-5441F51230F3}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {54382917-A916-4D9C-A4C9-5441F51230F3}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | EndGlobal 18 | -------------------------------------------------------------------------------- /python/create_export.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | """Demonstrates basic usage of the Merchant Data Export API 4 | Base Resource URL: /v3/merchants/{mId}/exports 5 | 6 | Script Usage 7 | ------------ 8 | Update these configuration variables defined in the script body: 9 | - HOST: The target API host 10 | - MERCHANT_ID: The Clover ID of the merchant whose data you want to export 11 | - ACCESS_TOKEN: A valid API token with access to the target merchant 12 | - EXPORT_TYPE: The type of data to export ('ORDERS' or 'PAYMENTS') 13 | - START_TIME: The start (lower-bound) of the export time window 14 | - END_TIME: The end (upper-bound) of the export time window 15 | 16 | $ python ./create_export.py 17 | """ 18 | 19 | import requests 20 | import time 21 | import sys 22 | import itertools 23 | 24 | 25 | # -- Begin Script Configuration -- 26 | 27 | HOST = "https://sandbox.dev.clover.com" 28 | MERCHANT_ID = "" 29 | ACCESS_TOKEN = "" 30 | 31 | EXPORT_TYPE = "ORDERS" # "ORDERS" or "PAYMENTS" 32 | START_TIME = "1554076800000" # 04/01/2019 @ 12:00am (UTC) 33 | END_TIME = "1556582400000" # 04/30/2019 @ 12:00am (UTC) 34 | 35 | # -- End Script Configuration -- 36 | 37 | 38 | SPINNER = itertools.cycle(["|", "/", "-", "\\"]) 39 | 40 | 41 | def main(): 42 | # Request the export 43 | export = create_export( 44 | export_type=EXPORT_TYPE, 45 | start_time=START_TIME, 46 | end_time=END_TIME, 47 | ) 48 | print_export(export, "Requested Export") 49 | 50 | # Wait for the export to finish processing 51 | export = wait_for_export(export["id"]) 52 | print_export(export, "Finished Export") 53 | 54 | # Get the URLs for the exported data 55 | export_urls = get_export_urls(export["id"]) 56 | print_export_urls(export_urls) 57 | 58 | # Download the data from the export URLs 59 | download_exported_data(export_urls) 60 | 61 | 62 | def create_export(export_type, start_time, end_time): 63 | """Request a new export""" 64 | url = api_url("/v3/merchants/" + MERCHANT_ID + "/exports/") 65 | 66 | payload = { 67 | "type": export_type, 68 | "startTime": start_time, 69 | "endTime": end_time, 70 | } 71 | resp = requests.post(url, json=payload) 72 | resp.raise_for_status() 73 | 74 | return resp.json() 75 | 76 | 77 | def get_export(export_id): 78 | """Get the current state of the specified export""" 79 | url = api_url("/v3/merchants/" + MERCHANT_ID + "/exports/" + export_id) 80 | 81 | resp = requests.get(url) 82 | resp.raise_for_status() 83 | 84 | return resp.json() 85 | 86 | 87 | def wait_for_export(export_id): 88 | """Block until the export is finished being processed""" 89 | while True: 90 | # Get the current export state 91 | export = get_export(export_id) 92 | 93 | # If the export is finished, stop waiting 94 | if export["status"] not in ("PENDING", "IN_PROGRESS"): 95 | print_export_status(export, True) 96 | break 97 | 98 | # If the export is not finished, sleep for a bit 99 | print_export_status(export) 100 | time.sleep(.25) 101 | 102 | return export 103 | 104 | 105 | def get_export_urls(export_id): 106 | """Extract the export URLs from the specified export""" 107 | export = get_export(export_id) 108 | 109 | return [u["url"] for u in export["exportUrls"]["elements"]] 110 | 111 | 112 | def download_exported_data(export_urls): 113 | """Download the exported data from the export URLs""" 114 | print("Downloaded Data") 115 | print("---------------") 116 | 117 | for export_url in export_urls: 118 | resp = requests.get(export_url) 119 | resp.raise_for_status() 120 | 121 | downloaded = resp.json()["elements"] 122 | 123 | # For the purposes of this demonstration, just print out the count. 124 | # In a real application, we would commit this data to some persistent 125 | # data store. 126 | count = len(downloaded) 127 | print("Downloaded: {}".format(count)) 128 | 129 | print("") 130 | 131 | 132 | def api_url(resource_path): 133 | """Generate the URL for an API call""" 134 | return "{}{}?access_token={}".format(HOST, resource_path, ACCESS_TOKEN) 135 | 136 | 137 | def print_export(export, label): 138 | """Pretty print the export""" 139 | print("") 140 | print(label) 141 | print("-" * len(label)) 142 | for k, v in export.items(): 143 | if k != "exportUrls": 144 | print("{}: {}".format(k, v)) 145 | print("") 146 | 147 | 148 | def print_export_status(export, finished=False): 149 | """Pretty print the export status""" 150 | spinner = "" if finished else next(SPINNER) 151 | print("({}%) Export is {}. {}".format(export['percentComplete'], export['status'], spinner)) 152 | sys.stdout.flush() 153 | 154 | 155 | def print_export_urls(export_urls): 156 | """Pretty print the export URLs""" 157 | print("Export URLs") 158 | print("-----------") 159 | 160 | for export_url in export_urls: 161 | print(export_url) 162 | print("") 163 | 164 | 165 | if __name__ == "__main__": 166 | main() 167 | -------------------------------------------------------------------------------- /python/requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.21.0 --------------------------------------------------------------------------------