Headers and Caching
Response Headers
The API will set the following HTTP response headers to values useful to developers:
Cache-ControlandExpires: These headers are set to conservative values for data caching purposes based on the data present in the response body.x-pch-env: The Punchh environment (integration, production, etc.)Etag: An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. If the resource content at that URL ever changes, a new and different ETag is assigned. Used in this manner, ETags are similar to fingerprints, and they can be quickly compared to determine whether two versions of a resource are the same.X-Request-Id: The Punchh API generates a unique request ID for every incoming HTTP request that it receives. This unique ID is then passed to your application as an HTTP header called X-Request-Id. This useful for debugging.X-Runtime: The server-side response time of the requestx-max-ios: The latest known iOS app version, to be used by iOS apps to determine whether version checking should be switched on or offx-max-android: The latest known Android app version, to be used by Android apps to determine whether version checking should be switched on or off
Request Headers
Please ensure that your implementation adheres to the following rules, as any messages that exceed these limits will be rejected by the server and will not be processed.
1. Request headers must be less than or equal to 32 KB in total.
2. Individual request headers must be less than or equal to 16 KB.
3. Do not send any additional request headers that are not specifically requested by the API specification.
The API respects the following headers:
Etag: An ETag is an opaque identifier assigned by a web server to a specific version of a resource found at a URL. If the resource content at that URL ever changes, a new and different ETag is assigned. Used in this manner, ETags are similar to fingerprints, and they can be quickly compared to determine whether two versions of a resource are the same.x-pch-digest: HMAC-SHA256 digest of URI and body. The header is used for security verification and protection against tampering during API calls between the mobile application and the Punchh server.punchh-app-device-id: The app device ID helps Punchh identify each device so that certain rewards can be awarded individually to each device instead of per user. For example, the sign-up reward is given to each device ID to prevent fraudulent sign-ups so that a user cannot do repeated sign-ups from a single device to get rewards. It should not change even if the user resets a device. So, it can be stored in some permanent storage such as a keychain in iOS. See the sample code to generate the punchh-app-device-id header.User-Agent: All mobile API requests MUST include a valid User-Agent header. The User-Agent header should contain information about the user's device. The request format isAppName/AppVersion/BuildNumber (OS; Model; MANUFACTURER; MODEL; OS Version). For example,MyAppName/1.2.5/17 (Android; ZTE; Z982; 7.1.1). This allows us to contact you if there are problems. Requests with no User-Agent header will be rejected. See the sample code in the following section to get the user agent information for a device. See User Agent for information about the user agent formats for IOS and Android devices.Accept-Encoding: We recommend using this as it will make responses much smaller over the wire. To enable it, add anAccept-Encoding: gzipheader to your request. Most HTTP client libraries wrap this functionality for you; consult your library’s documentation for details.Accept-Language: By default, the header takes "en" (English) as the language. Otherwise, you can also send other languages like Spanish (locale code "es").Accept-Timezone: We require you to supply a Time-Zone header that defines a time zone according to the list of names from the Olson database. This header will determine the time zone used for generating timestamps. We generate timestamps when an API call is made in the time zone defined by this header.Authorization: In POST requests, send "access_token" in this header (e.g.,Authorization: Bearer ACCESS_TOKEN_GOES_HERE).
Generating the punchh-app-device-id Header
You can use the following sample functions to generate a unique ID for the device. This unique ID will then persist on the device. This unique ID is passed in the punchh-app-device-id header. The following sample code is for demo purposes only and is distributed without any warranty. Review the code before use. The Java section contains the sample code to generate a unique device ID for an Android device, and the Objective-C section contains the sample code for an IOS device. See Maintain Unique Device ID for Mobile Devices.
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.punchh.services.DeviceResourceHandler;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Random;
import java.util.UUID;
public class DeviceId {
private static final String FIXED_ANDROID_ID = "ID_GOES_HERE";
private static final String INSTALLATION = "CONSTANT_INSTALLATION_APP_GENERATED_ID";
public static final String DEVICE_ID = "DEVICE_ID";
// private static String sID = null;
public synchronized static String generateDeviceId(Context context) {
String deviceId = DeviceResourceHandler.getInstance(context).getDataFromSharedPref(DEVICE_ID);
if (TextUtils.isEmpty(deviceId)) {
File installation = new File(context.getFilesDir(), INSTALLATION);
try {
if (!installation.exists()) {
UUID deviceUuid = new UUID(getDeviceUniqueId(context).hashCode(), context.getApplicationContext()
.getPackageName().hashCode());
deviceId = deviceUuid.toString();
writeInstallationFile(installation, deviceId);
DeviceResourceHandler.getInstance(context).addToSharedPref(DEVICE_ID, deviceId);
return deviceId;
}
deviceId = readInstallationFile(installation);
DeviceResourceHandler.getInstance(context).addToSharedPref(DEVICE_ID, deviceId);
} catch (Exception e) {
e.printStackTrace();
}
}
return deviceId;
}
public static String getId(Context context) {
return DeviceResourceHandler.getInstance(context).getDataFromSharedPref(DEVICE_ID);
}
private static String readInstallationFile(File installation) throws IOException {
RandomAccessFile f = new RandomAccessFile(installation, "r");
byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
f.close();
return new String(bytes);
}
private static void writeInstallationFile(File installation, String id) throws IOException {
FileOutputStream out = new FileOutputStream(installation);
out.write(id.getBytes());
out.close();
}
private static String getDeviceUniqueId(Context context) {
TelephonyManager mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String deviceIMEI = null;
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED
&& mTelephonyManager != null) {
deviceIMEI = mTelephonyManager.getDeviceId();
}
if (deviceIMEI == null || deviceIMEI.equals("")) {
return getAndroidId(context);
} else {
return deviceIMEI;
}
}
private static String getAndroidId(Context context) {
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
if (androidId.equalsIgnoreCase(FIXED_ANDROID_ID)) {
return getSerialBuildId();
} else
return androidId;
}
private static String getSerialBuildId() {
if (!TextUtils.isEmpty(android.os.Build.SERIAL)) {
return android.os.Build.SERIAL;
} else
return getRandomId();
}
private static String getRandomId() {
//If nothing is found for this device, generate a random number, although this case is not possible.
Random tmpRand = new Random();
return String.valueOf(tmpRand.nextLong());
}
}
/// 1234567890: APP_DEVICE_UUID_GOES_HERE-1234567890
+ (NSString *)getDeviceIdentifier {
NSString *iTunesAppId = [NSBundle iTunesAppleID];
NSString *identifier = [UICKeyChainStore stringForKey:iTunesAppId];
if (![identifier present]) {
identifier = [NSString stringWithFormat:@"%@-%@", [[NSUUID UUID] UUIDString], iTunesAppId];
[UICKeyChainStore setString:identifier forKey:iTunesAppId];
}
return identifier;
}
Getting User Agent Information for a User's Mobile Device
You can use the following sample functions to get the user agent information for the user's device. This information is then passed into the User-Agent header in the API request. The Java section contains the sample code to get the user agent information for an Android device, and the Objective-C section contains the sample code for an IOS device.
/**
* Gets user agent.
*
* @return the user agent
*/
public static String getUserAgent() {
return "MyAppName" + "/"
+ "1.2.5" + "/" + "17" + " (Android; " + android.os.Build.MANUFACTURER + "; "
+ android.os.Build.MODEL + "; " + android.os.Build.VERSION.RELEASE + ")";
}
/// MyRewards/2.0/2 (iPhone8,2; iOS 10.3; Scale/2.00)
- (NSString *)userAgent {
return [NSString stringWithFormat:@"%@/%@/%@ (%@; %@ %@; Scale/%0.2f)",
[NSBundle appName],
[NSBundle appVersion],
[NSBundle appBuildNo],
[[UIDevice currentDevice] modelIdentifier],
[[UIDevice currentDevice] systemName],
[[UIDevice currentDevice] systemVersion],
[[UIDevice currentDevice] scaleSize]];
}