package com.urbanairship.digitalwallet.client; import com.google.gson.Gson; import com.urbanairship.digitalwallet.client.data.LocationInfo; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.util.*; /* * Method Path Description * GET / List all passes for this user * GET /{strPassId} Get the pass based on the pass id * GET /id/{externalId} Get the pass based on the external id * GET /{strPassId}/viewJSONPass View the Apple JSON for a pass based on its id * GET /id/{externalId}/viewJSONPass View the Apple JSON for a pass based on its external id * GET /{strPassId}/download Download the pkPass for tha apple pass, based on its id * GET /id/{externalId}/download Download the pkPass for tha apple pass, based on its external id * GET /{strPassId}/tags Get the tags for the specified pass based on the pass id * GET /id/{externalId}/tags Get the tags for the specified pass based on the external id * POST /{strTemplateId} Create a pass on that template, based on the template id. * POST /{strTemplateId}/id/{externalId} Create a pass on that template, and give the pass an external id. * POST /id/{strTemplateExternalId}/id/{externalId} Create a pass on a template, based on the templates external id and give the pass an external id. * POST /{strPassId}/locations Add a location to a pass * POST /id/{externalId}/locations Add a location to a pass based on the pass' external id. * PUT /{strPassId}/tags Add tags to the specified pass, based on the pass' id. * PUT /id/{externalId}/tags Add tags to the specified pass, based on the pass' external id. * PUT /{strPassId} Update the pass specified by the pass id. * PUT /id/{externalId} Update the pass specified by the external id. * PUT /{strPassId}/push Tell Apple that the pass has been updated, based on pass' id. * PUT /id/{externalId}/push Tell Apple that the pass has been updated, based on pass' external id. * DELETE /{strPassId} Delete the pass specified by that pass id. * DELETE /id/{externalId} Delete the pass specified by that external id. * DELETE /{strPassId}/location/{strLocationId} Delete the location on the pass specified by pass id. * DELETE /id/{externalId}/location/{strLocationId} Delete the location on the pass specified by external id. */ public class Pass extends PassToolsClient { /*********** * properties ***********/ private Map fields; // (field key, JSONObject ) private Map headers; //headers for 1.1+ private Long passId; private Long templateId; private String url; private String externalId; private static final String missingPassFieldsError = "please pass a map of fields in!"; private static final String missingPassError = "please input a valid Pass!"; private static final String missingExternalId = "please pass a valid external id in!"; private static final String missingTagsError = "please pass in a valid list of tags"; private static final String missingExternalTemplateIdError = "please pass a valid external template id in!"; /*********** * constructors ***********/ public Pass() { } public Pass(JSONObject response) { assign(response); } private Pass(PassToolsResponse response) { assign(response.getBodyAsJSONObject()); } /*********** * methods ***********/ /** * List your passes. * * @param pageSize The number of passes you want returned per request. * @param page The page you want returned, starting with 1. * @return A list of passes. */ public static List<Pass> listPasses(int pageSize, int page) { try { List<Pass> passes = new ArrayList<Pass>(); StringBuilder builder = new StringBuilder(getBaseUrl()); builder.append("?pageSize=").append(pageSize).append("&page=").append(page); PassToolsResponse response = get(builder.toString()); JSONObject jsonResponse = response.getBodyAsJSONObject(); JSONArray passArray = (JSONArray) jsonResponse.get("passes"); if (passArray != null) { for (Object o : passArray.toArray()) { if (o instanceof JSONObject) { passes.add(new Pass((JSONObject) o)); } } } return passes; } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * Get the tags associated with the specified pass. * * @param passId Pass you want the tags of. * @return A list of tags that the specified pass is a member of. */ public static List<Tag> getTags(long passId) { return getTagsInternal(getBaseUrl(passId)); } /** * Get the tags associated with the specified pass. * * @param externalId Pass you want the tags of. * @return A list of tags that the specified pass is a member of. */ public static List<Tag> getTags(String externalId) { checkNotNull(externalId, missingExternalId); return getTagsInternal(getBaseUrl(externalId)); } /** * Add the specified pass to the specified list of tags. * * @param passId pass you want to add to the tags. * @param tags List of tags you want the pass added to. * @return A list of tags that the pass was added to. * Not including tags that the list already belonged to. */ public static List<String> addTags(long passId, List<String> tags) { checkNotNull(tags, missingTagsError); return addTagsInternal(getBaseUrl(passId), tags); } /** * Add the specified pass to the specified list of tags. * * @param externalId pass you want to add to the tags. * @param tags List of tags you want the pass added to. * @return A list of tags that the pass was added to. * Not including tags that the list already belonged to. */ public static List<String> addTags(String externalId, List<String> tags) { checkNotNull(externalId, missingExternalId); checkNotNull(tags, missingTagsError); return addTagsInternal(getBaseUrl(externalId), tags); } /** * Add the specified pass to the specified tag. * * @param passId ID of the pass you want to add to the tag. * @param tag tag you want to add the pass to. * @return a list of tags, either empty or 1 entry. * If the pass already belonged to the tag then 1 entry, otherwise empty. */ public static List<String> addTag(long passId, String tag) { checkNotNull(tag, missingTagsError); return addTagInternal(getBaseUrl(passId), tag); } /** * Add the specified pass to the specified tag. * * @param externalId External ID of the pass you want to add to the tag. * @param tag tag you want to add the pass to. * @return a list of tags, either empty or 1 entry. * If the pass already belonged to the tag then 1 entry, otherwise empty. */ public static List<String> addTag(String externalId, String tag) { checkNotNull(externalId, missingExternalId); checkNotNull(tag, missingTagsError); return addTagInternal(getBaseUrl(externalId), tag); } /** * Creates a pass with the Map fieldsModel set. The Map fieldsModel can be retrieved from the getTemplateModel() function given a templateId provided by the UI Template Builder * * @param templateId Template you want to create the new pass on. * @param passFields Updated fields for this pass, call getTemplateModel() then update the fields for this pass. * @return The newly created pass. */ public static Pass create(long templateId, Map passFields) { checkNotNull(passFields, missingPassFieldsError); return createInternal(getBaseUrl(templateId), passFields); } /** * Creates a pass with the Map fieldsModel set. The Map fieldsModel can be retrieved from the getTemplateModel() function given a templateId provided by the UI Template Builder. * The newly created pass will have the passExternalId assigned to it. * * @param templateId Template you want to create the new pass on. * @param passFields Updated fields for this pass, call getTemplateModel() then update the fields for this pass. * @param passExternalId External ID you want assigned to this pass. * @return The newly created pass. */ public static Pass create(long templateId, String passExternalId, Map passFields) { checkNotNull(passFields, missingPassFieldsError); checkNotNull(passExternalId, missingExternalId); return createInternal(getBaseUrl(templateId) + "/id/" + passExternalId, passFields); } /** * Creates a pass with the Map fieldsModel set. The Map fieldsModel can be retrieved from the getTemplateModel() function given a templateId provided by the UI Template Builder. * The newly created pass will have the passExternalId assigned to it. * * @param templateExternalId Template you want to create the new pass on. * @param passExternalId External ID you want assigned to this pass. * @param passFields Updated fields for this pass, call getTemplateModel() then update the fields for this pass. * @return The newly created pass. */ public static Pass create(String templateExternalId, String passExternalId, Map passFields) { checkNotNull(templateExternalId, missingExternalTemplateIdError); checkNotNull(passFields, missingPassFieldsError); checkNotNull(passExternalId, missingExternalId); return createInternal(getBaseUrl(templateExternalId) + "/id/" + passExternalId, passFields); } /** * Update the specified pass. * * @param passId ID of the pass you want to update. * @param fields Updated fields for the pass, call getPass first then update the fields. * @return The updated Pass. */ @SuppressWarnings("unchecked") public static Pass update(long passId, Map fields) { checkNotNull(fields, missingPassFieldsError); try { String url = getBaseUrl(passId); JSONObject jsonObj = new JSONObject(fields); Map formFields = new HashMap<String, Object>(); formFields.put("json", jsonObj); return new Pass(put(url, formFields)); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * Update the specified pass. * * @param externalId external Id of the pass you want to update. * @param fields Updated fields for the pass, call getPass first then update the fields. * @return The updated Pass. */ @SuppressWarnings("unchecked") public static Pass update(String externalId, Map fields) { checkNotNull(externalId, missingExternalId); checkNotNull(fields, missingPassFieldsError); try { String url = getBaseUrl(externalId); JSONObject jsonObj = new JSONObject(fields); Map formFields = new HashMap<String, Object>(); formFields.put("json", jsonObj); return new Pass(put(url, formFields)); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * Update the specified pass. * * @param pass Pass you want to update, with the fields already updated. * Call getPass, update the fields on the returned pass, then call update with the modified pass. */ @SuppressWarnings("unchecked") public static void update(Pass pass) { checkNotNull(pass, missingPassError); checkNotNull(pass.passId, missingPassError); try { String url = getBaseUrl(pass.passId); JSONObject jsonObj = new JSONObject(pass.fields); Map formFields = new HashMap<String, Object>(); formFields.put("json", jsonObj); PassToolsResponse response = put(url, formFields); JSONObject jsonObjResponse = response.getBodyAsJSONObject(); pass.url = (String) jsonObjResponse.get("url"); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * Get the specified pass. * * @param passId Id of the pass you want to get. * @return the pass. */ public static Pass get(long passId) { try { String url = getBaseUrl(passId); PassToolsResponse response = get(url); return new Pass(response.getBodyAsJSONObject()); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * Get the specified pass. * * @param externalId external id of the pass you want to get. * @return the pass. */ public static Pass getPass(String externalId) { checkNotNull(externalId, missingExternalId); try { String url = getBaseUrl(externalId); PassToolsResponse response = get(url); return new Pass(response.getBodyAsJSONObject()); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * Download the specified pass. Apple passes only. * * @param passId ID of the pass you want to download * @param to File you want the pass written to. */ public static void downloadPass(long passId, File to) { downloadPassInternal(getBaseUrl(passId) + "/download", to); } /** * Download the specified pass. Apple passes only. * * @param externalId ID of the pass you want to download * @param to File you want the pass written to. */ public static void downloadPass(String externalId, File to) { checkNotNull(externalId, missingExternalId); downloadPassInternal(getBaseUrl(externalId) + "/download", to); } /** * Delete the specified pass. * * @param passId ID of the pass you want to delete. */ public static void delete(long passId) { try { String url = getBaseUrl(passId); PassToolsResponse response = delete(url); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * Delete the specified pass. * * @param externalId ID of the pass you want to delete. */ public static void deleteX(String externalId) { checkNotNull(externalId, missingExternalId); try { String url = getBaseUrl(externalId); PassToolsResponse response = delete(url); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * View the JSON for the specified pass. This is the same JSON you would use if you were calling Apple PassKit directly. * * @param passId Id of the pass you want the json of. * @return The JSON for the specified pass, same JSON you would use if you were calling Apple PassKit directly. */ public static JSONObject viewPassbookJSONPass(long passId) { try { String url = getBaseUrl(passId) + "/viewJSONPass"; PassToolsResponse response = get(url); return response.getBodyAsJSONObject(); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * View the JSON for the specified pass. This is the same JSON you would use if you were calling Apple PassKit directly. * * @param externalId Id of the pass you want the json of. * @return The JSON for the specified pass, same JSON you would use if you were calling Apple PassKit directly. */ public static JSONObject viewPassbookJSONPass(String externalId) { checkNotNull(externalId, missingExternalId); try { String url = getBaseUrl(externalId) + "/viewJSONPass"; PassToolsResponse response = get(url); return response.getBodyAsJSONObject(); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * Tell Apple that the pass has been updated, based on pass' id. * * @param passId ID of the pass you want to push. * @return A JSON object with the list of devices that were updated. */ public static JSONObject push(long passId) { try { String url = getBaseUrl(passId) + "/push"; PassToolsResponse response = put(url, Collections.emptyMap()); return response.getBodyAsJSONObject(); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * Tell Apple that the pass has been updated, based on pass' external id. * * @param externalId External ID of the pass you want to push. * @return A JSON object with the list of devices that were updated. */ public static JSONObject push(String externalId) { checkNotNull(externalId, missingExternalId); try { String url = getBaseUrl(externalId) + "/push"; PassToolsResponse response = put(url, Collections.emptyMap()); return response.getBodyAsJSONObject(); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * Add the specified locations to the specified pass. * * @param passId Pass you want to add the locations to. * @param locationInfo Locations you want added. * @return A list of locations * [ * { * "passLocationId":65, * "value":{ * "region":"CA", * "regionCode":"94404", * "relevantText":"Hello loc0!", * "streetAddress1":"add11", * "streetAddress2":"add22", * "longitude":-122.3742 * "latitude":37.618, * "city":"FC" * } * }, * { * "passLocationId":66, * "value":{ * "region":"CA", * "regionCode":"94404", * "relevantText":"Hello loc1!", * "streetAddress1":"add12", * "streetAddress2":"add23", * "longitude":-123.374, * "latitude":38.618, * "city":"FC" * } * } * ] */ public static JSONArray addLocations(long passId, List<LocationInfo> locationInfo) { return addLocationsInternal(getBaseUrl(passId) + "/locations", locationInfo); } /** * Add the specified locations to the specified pass. * * @param externalId Pass you want to add the locations to. * @param locationInfo Locations you want added. * @return A list of locations * [ * { * "passLocationId":65, * "value":{ * "region":"CA", * "regionCode":"94404", * "relevantText":"Hello loc0!", * "streetAddress1":"add11", * "streetAddress2":"add22", * "longitude":-122.3742 * "latitude":37.618, * "city":"FC" * } * }, * { * "passLocationId":66, * "value":{ * "region":"CA", * "regionCode":"94404", * "relevantText":"Hello loc1!", * "streetAddress1":"add12", * "streetAddress2":"add23", * "longitude":-123.374, * "latitude":38.618, * "city":"FC" * } * } * ] */ public static JSONArray addLocations(String externalId, List<LocationInfo> locationInfo) { checkNotNull(externalId, missingExternalId); return addLocationsInternal(getBaseUrl(externalId) + "/locations", locationInfo); } /** * deletes a location from the pass * * @param passId id of the pass you want to remove the location from. * @param passLocationId id of the location you want to remove. */ public static void deleteLocation(long passId, long passLocationId) { try { String url = getBaseUrl(passId) + "/location/" + String.valueOf(passLocationId); delete(url); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /** * deletes a location from the pass * * @param externalId id of the pass you want to remove the location from. * @param passLocationId id of the location you want to remove. */ public static void deleteLocation(String externalId, long passLocationId) { checkNotNull(externalId, missingExternalId); try { String url = getBaseUrl(externalId) + "/location/" + String.valueOf(passLocationId); delete(url); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } /*********** * getters ***********/ public Map getFields() { return fields; } public Map getHeaders() { return headers; } public Long getPassId() { return passId; } public Long getTemplateId() { return templateId; } public String getUrl() { return url; } public String getExternalId() { return externalId; } /*********** * private methods ***********/ private void reset() { passId = null; templateId = null; externalId = null; url = null; fields = null; headers = null; } @SuppressWarnings("unchecked") private void assign(JSONObject response) { reset(); if (response != null) { passId = toLong(response.get("id")); templateId = toLong(response.get("templateId")); if (response.get("externalId") != null) { externalId = (String) response.get("externalId"); } url = (String) response.get("url"); if (response.get("fields") != null) { fields = (Map<String, String>) (response.get("fields")); } if (response.get("headers") != null) { headers = (Map<String, String>) (response.get("headers")); } } } private static String getBaseUrl() { return PassTools.API_BASE + "/pass"; } private static String getBaseUrl(long passId) { return PassTools.API_BASE + "/pass/" + passId; } private static String getBaseUrl(String externalId) { return PassTools.API_BASE + "/pass/id/" + externalId; } private static void downloadPassInternal(String url, File to) { if (to == null || !to.exists()) { throw new IllegalArgumentException("please pass a valid file in!"); } try { PassToolsResponse response = get(url); InputStream is = response.response.getEntity().getContent(); FileOutputStream fos = new FileOutputStream(to); try { int c; while ((c = is.read()) != -1) { fos.write(c); } } finally { is.close(); fos.close(); } } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } @SuppressWarnings("unchecked") private static JSONArray addLocationsInternal(String url, List<LocationInfo> locationInfo) { try { JSONArray array = new JSONArray(); Gson gson = new Gson(); for (LocationInfo currentLocation : locationInfo) { array.add(gson.toJson(currentLocation)); } Map formFields = new HashMap<String, JSONArray>(); formFields.put("json", array); PassToolsResponse response = post(url, formFields); return response.getBodyAsJSONArray(); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } private static List<Tag> getTagsInternal(String url) { try { PassToolsResponse response = get(url); JSONObject json = response.getBodyAsJSONObject(); JSONArray jsonTags = (JSONArray)json.get("tags"); List<Tag> tags = new ArrayList<Tag>(); for (Object o : jsonTags.toArray()) { if (o instanceof JSONObject) { tags.add(new Tag((JSONObject)o)); } } return tags; } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } private static List<String> addTagInternal(String url, String tag) { List<String> tags = new ArrayList<String>(); tags.add(tag); return addTagsInternal(url, tags); } @SuppressWarnings("unchecked") private static List<String> addTagsInternal(String url, List<String> tags) { url += "/tags"; try { JSONObject json = new JSONObject(); JSONObject tagsObj = new JSONObject(); JSONArray jsonTags = new JSONArray(); for (String currentTag : tags) { jsonTags.add(currentTag); } tagsObj.put("tags", jsonTags); json.put("json", tagsObj); PassToolsResponse response = put(url, json); JSONObject jsonResponse = response.getBodyAsJSONObject(); JSONArray newTags = (JSONArray)jsonResponse.get("newTags"); List<String> addedTags = new ArrayList<String>(); for (Object currentObj : newTags.toArray()) { if (currentObj instanceof String) { addedTags.add((String)currentObj); } } return addedTags; } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } @SuppressWarnings("unchecked") private static Pass createInternal(String url, Map fields) { try { JSONObject jsonObj = new JSONObject(fields); Map formFields = new HashMap<String, Object>(); formFields.put("json", jsonObj); return new Pass(post(url, formFields)); } catch (RuntimeException rte) { throw rte; } catch (Exception e) { throw new RuntimeException(e); } } }