Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added reportOrder to ButtonMerchant #25

Merged
merged 4 commits into from Jun 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
112 changes: 112 additions & 0 deletions button-merchant/src/main/java/com/usebutton/merchant/ApiRequest.java
@@ -0,0 +1,112 @@
/*
* ApiRequest.java
*
* Copyright (c) 2019 Button, Inc. (https://usebutton.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package com.usebutton.merchant;

import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

/**
* Api request model for {@link ConnectionManager}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: API*

*/
class ApiRequest {

/**
* Request Method for the api request
*/
enum RequestMethod {
POST("POST");

private final String value;

RequestMethod(String value) {
this.value = value;
}

String getValue() {
return value;
}
}

private final RequestMethod requestMethod;
private final String path;
private final Map<String, String> headers;
private final JSONObject body;

private ApiRequest(Builder builder) {
this.requestMethod = builder.requestMethod;
this.path = builder.path;
this.headers = builder.headers;
this.body = builder.body;
}

RequestMethod getRequestMethod() {
return requestMethod;
}

String getPath() {
return path;
}

Map<String, String> getHeaders() {
return headers;
}

JSONObject getBody() {
return body;
}

/**
* Constructor
*/
static class Builder {

private final RequestMethod requestMethod;
private final String path;
private Map<String, String> headers = new HashMap<>();
private JSONObject body = new JSONObject();

Builder(RequestMethod requestMethod, String path) {
this.requestMethod = requestMethod;
this.path = path;
}

Builder addHeader(String key, String value) {
headers.put(key, value);
return this;
}

Builder setBody(JSONObject body) {
this.body = body;
return this;
}

ApiRequest build() {
return new ApiRequest(this);
}
}
}
Expand Up @@ -47,4 +47,9 @@ PostInstallLink getPendingLink(String applicationId, String ifa,
@WorkerThread
Void postActivity(String applicationId, String sourceToken, String timestamp, Order order)
throws ButtonNetworkException;

@Nullable
@WorkerThread
Void postOrder(Order order, String applicationId, String sourceToken,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why postOrder and not reportOrder?
Edit: I see reportOrder else where so I don't know what this method is

Copy link
Contributor Author

@Jon6193 Jon6193 Jun 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming convention throughout the entire ButtonApi class is requestMethod + endpoint. reportOrder is the public facing method

@Nullable String advertisingId) throws ButtonNetworkException;
}
124 changes: 109 additions & 15 deletions button-merchant/src/main/java/com/usebutton/merchant/ButtonApiImpl.java
Expand Up @@ -32,9 +32,11 @@

import com.usebutton.merchant.exception.ButtonNetworkException;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.List;
import java.util.Map;

/**
Expand Down Expand Up @@ -69,14 +71,19 @@ public PostInstallLink getPendingLink(String applicationId, String ifa,

try {
// Create request body
JSONObject request = new JSONObject();
request.put("application_id", applicationId);
request.put("ifa", ifa);
request.put("ifa_limited", limitAdTrackingEnabled);
request.put("signals", new JSONObject(signalsMap));
JSONObject requestBody = new JSONObject();
requestBody.put("application_id", applicationId);
requestBody.put("ifa", ifa);
requestBody.put("ifa_limited", limitAdTrackingEnabled);
requestBody.put("signals", new JSONObject(signalsMap));

ApiRequest apiRequest = new ApiRequest.Builder(ApiRequest.RequestMethod.POST,
"/v1/web/deferred-deeplink")
.setBody(requestBody)
.build();

// Execute POST request and parse response
NetworkResponse response = connectionManager.post("/v1/web/deferred-deeplink", request);
NetworkResponse response = connectionManager.executeRequest(apiRequest);
JSONObject responseBody = response.getBody().optJSONObject("object");
if (responseBody != null) {
boolean match = responseBody.getBoolean("match");
Expand Down Expand Up @@ -107,17 +114,104 @@ public Void postActivity(String applicationId, String sourceToken, String timest

try {
// Create request body
JSONObject request = new JSONObject();
request.put("app_id", applicationId);
request.put("user_local_time", timestamp);
request.put("btn_ref", sourceToken);
request.put("order_id", order.getId());
request.put("total", order.getAmount());
request.put("currency", order.getCurrencyCode());
request.put("source", "merchant-library");
JSONObject requestBody = new JSONObject();
requestBody.put("app_id", applicationId);
requestBody.put("user_local_time", timestamp);
requestBody.put("btn_ref", sourceToken);
requestBody.put("order_id", order.getId());
requestBody.put("total", order.getAmount());
requestBody.put("currency", order.getCurrencyCode());
requestBody.put("source", "merchant-library");

ApiRequest apiRequest = new ApiRequest.Builder(ApiRequest.RequestMethod.POST,
"/v1/activity/order")
.setBody(requestBody)
.build();

// Execute POST request and parse response
connectionManager.post("/v1/activity/order", request);
connectionManager.executeRequest(apiRequest);
} catch (JSONException e) {
Log.e(TAG, "Error creating request body", e);
throw new ButtonNetworkException(e);
}

return null;
}

@Nullable
@Override
public Void postOrder(Order order, String applicationId, String sourceToken,
@Nullable String advertisingId) throws ButtonNetworkException {

try {
// Create request body
JSONObject requestBody = new JSONObject();
requestBody.put("currency", order.getCurrencyCode());
requestBody.put("btn_ref", sourceToken);
requestBody.put("order_id", order.getId());
requestBody.put("purchase_date", ButtonUtil.formatDate(order.getPurchaseDate()));
requestBody.put("customer_order_id", order.getCustomerOrderId());

for (Order.LineItem lineItem : order.getLineItems()) {
JSONArray lineItemsJson = new JSONArray();
JSONObject lineItemJson = new JSONObject();

List<String> lineItemCategory = lineItem.getCategory();
if (lineItemCategory != null) {
JSONArray categoryJson = new JSONArray();
for (String category : lineItemCategory) {
categoryJson.put(category);
}

lineItemJson.put("category", categoryJson);
}

lineItemJson.put("identifier", lineItem.getId());
lineItemJson.put("quantity", lineItem.getQuantity());
lineItemJson.put("total", lineItem.getTotal());

Map<String, String> lineItemAttributes = lineItem.getAttributes();
if (lineItemAttributes != null) {
JSONObject attributesJson = new JSONObject();
for (Map.Entry<String, String> entry : lineItemAttributes.entrySet()) {
attributesJson.put(entry.getKey(), entry.getValue());
}

lineItemJson.put("attributes", attributesJson);
}

lineItemJson.put("upc", lineItem.getUpc());
lineItemJson.put("description", lineItem.getDescription());
lineItemJson.put("sku", lineItem.getSku());

lineItemsJson.put(lineItemJson);
requestBody.put("line_items", lineItemsJson);
}

Order.Customer customer = order.getCustomer();
if (customer != null) {
JSONObject customerJson = new JSONObject();
customerJson.put("id", customer.getId());

String email = customer.getEmail();
if (email != null) {
// TODO ADD EMAIL REGEX CHECK
email = ButtonUtil.sha256Encode(email.toLowerCase());
customerJson.put("email_sha256", email);
}

customerJson.put("device_id", advertisingId);
requestBody.put("customer", customerJson);
}

applicationId = ButtonUtil.base64Encode(applicationId + ":");
ApiRequest apiRequest = new ApiRequest.Builder(ApiRequest.RequestMethod.POST,
"/v1/mobile-order")
.addHeader("Authorization", String.format("Basic %s", applicationId))
.setBody(requestBody)
.build();

connectionManager.executeRequest(apiRequest);
} catch (JSONException e) {
Log.e(TAG, "Error creating request body", e);
throw new ButtonNetworkException(e);
Expand Down
Expand Up @@ -57,4 +57,7 @@ void removeAttributionTokenListener(ButtonRepository buttonRepository, @NonNull

void handlePostInstallIntent(ButtonRepository buttonRepository,
PostInstallIntentListener listener, String packageName, DeviceManager deviceManager);

void reportOrder(ButtonRepository buttonRepository, DeviceManager deviceManager, Order order,
@Nullable OrderListener orderListener);
}
Expand Up @@ -225,6 +225,39 @@ public void run() {
}, deviceManager);
}

@Override
public void reportOrder(ButtonRepository buttonRepository, DeviceManager deviceManager,
Order order, @Nullable final OrderListener orderListener) {

if (buttonRepository.getApplicationId() == null) {
executor.execute(new Runnable() {
@Override
public void run() {
if (orderListener != null) {
orderListener.onResult(new ApplicationIdNotFoundException());
}
}
});
return;
}

buttonRepository.postOrder(order, deviceManager, new Task.Listener() {
@Override
public void onTaskComplete(@Nullable Object object) {
if (orderListener != null) {
orderListener.onResult(null);
}
}

@Override
public void onTaskError(Throwable throwable) {
if (orderListener != null) {
orderListener.onResult(throwable);
}
}
});
}

/**
* Sets the attribution token in the {@link ButtonRepository} and updates all
* {@link ButtonInternalImpl#attributionTokenListeners} if the token has changed.
Expand Down
Expand Up @@ -78,7 +78,8 @@ public static void trackIncomingIntent(@NonNull Context context, @NonNull Intent
*
* @param order {@link Order}
* @param userActivityListener {@link UserActivityListener}
* @deprecated
*
* @deprecated Use {@link ButtonMerchant#reportOrder(Context, Order, OrderListener)}
*/
@Deprecated
public static void trackOrder(@NonNull Context context, @NonNull Order order,
Expand All @@ -87,6 +88,20 @@ public static void trackOrder(@NonNull Context context, @NonNull Order order,
userActivityListener);
}

/**
* Report orders
*
* @param context a {@link Context) instance that can be used to access app resources like
* SharedPreferences.
* @param order {@link Order}
* @param orderListener {@link OrderListener}
*/
public static void reportOrder(@NonNull Context context, @NonNull Order order,
@Nullable OrderListener orderListener) {
buttonInternal.reportOrder(getButtonRepository(context), getDeviceManager(context), order,
orderListener);
}

/**
* The {@code attributionToken} from the last inbound Button attributed {@link Intent}.
*
Expand Down
Expand Up @@ -51,4 +51,6 @@ interface ButtonRepository {
boolean checkedDeferredDeepLink();

void updateCheckDeferredDeepLink(boolean checkedDeferredDeepLink);

void postOrder(Order order, DeviceManager deviceManager, Task.Listener listener);
}
Expand Up @@ -100,10 +100,10 @@ public void getPendingLink(Task.Listener<PostInstallLink> listener,
}

@Override
public void postUserActivity(DeviceManager manager, Order order, Task.Listener listener) {
public void postUserActivity(DeviceManager deviceManager, Order order, Task.Listener listener) {
executorService.submit(
new UserActivityTask(listener, buttonApi, getApplicationId(), getSourceToken(),
manager, order));
deviceManager, order));
}

@Override
Expand All @@ -115,4 +115,11 @@ public boolean checkedDeferredDeepLink() {
public void updateCheckDeferredDeepLink(boolean checkedDeferredDeepLink) {
persistenceManager.updateCheckDeferredDeepLink(checkedDeferredDeepLink);
}

@Override
public void postOrder(Order order, DeviceManager deviceManager, Task.Listener listener) {
executorService.submit(
new PostOrderTask(listener, buttonApi, order, getApplicationId(),
getSourceToken(), deviceManager));
}
}