Alipay+ DocsAlipay+ Docs

Obtain the device token ID

To use the getPaymentCode API, you must include the device token ID (the deviceTokenId parameter) in the env parameter in the request. This topic provides information on the value of the parameter and how to obtain it.

Rules about the value of the env.deviceTokenId parameter

The following rules are applied to the value of the deviceTokenId parameter:

  • The device token IDs of each device are unique.
  • The device token ID stays the same on a device until the app is uninstalled. If the app is reinstalled, the device token ID can change.
  • The device token IDs vary for different apps on the same device.
  • The device token ID can consist of numbers, characters, and the hyphen symbol (-).
  • You can obtain the device token ID quietly, without requesting permission from the user. You also do not need to provide permission declarations in the app project.

How to obtain the device token ID

iOS

The following code is a sample you can run in your app to obtain the device token ID.

  • It is preferred that you use the identifier for vendors (IDFV) as the device token ID. The operating system directly generates the IDFV. For example, E621E1F8-C36C-495A-93FC-0C247A3E6E5F.
  • Theoretically, the operating system can fail to generate the IDFV. If that occurs, use the fallback method to generate a random ID. The random ID format is timestamp + random values. For example, 1673576834-66646241-27151131.
  • Once the device token ID is generated, it is saved in the local storage. Next time the app is initiated, the saved device token ID is read first, unless the app is uninstalled.
copy
+ (NSString *)getDeviceTokenId {
    static NSString *deviceTokenId;
    if (deviceTokenId.length) {
        return deviceTokenId;
    }
    
    deviceTokenId = [[NSUserDefaults standardUserDefaults] stringForKey:@"YOUR_DEVICE_TOKEN_ID_KEY"];
    if (deviceTokenId.length) {
        return deviceTokenId;
    }
    
    deviceTokenId = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
    if (!deviceTokenId.length) {
        long long timeStamp = (long long)[[NSDate date] timeIntervalSince1970];
        long long random0 = arc4random() % 100;
        long long random1 = arc4random() % 100;
        long long random2 = arc4random() % 100;
        long long random3 = arc4random() % 100;
        long long random4 = arc4random() % 100;
        long long random5 = arc4random() % 100;
        long long random6 = arc4random() % 100;
        long long random7 = arc4random() % 100;
        
        deviceTokenId = [NSString stringWithFormat:@"%lld-%lld%lld%lld%lld-%lld%lld%lld%lld", timeStamp, random0, random1, random2,random3,random4, random5, random6,random7];
    }
    
    [[NSUserDefaults standardUserDefaults] setObject:deviceTokenId forKey:@"YOUR_DEVICE_TOKEN_ID_KEY"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    return deviceTokenId;
}

Android

The following code is a sample you can run in your app to obtain the device token ID.

  • It is preferred that you use the ANDROID_ID as the device token ID, for example, 611fd8def6a78561. For more information about the ANDROID_ID, see Android 8.0 Behavior Changes.
  • If the ANDROID_ID is empty, use either of the following fallback methods to generate a random ID according to your needs:
    • getDeviceIdA: generates a random UUID, such as 9e146147-a0c6-4781-bedf-a433af21a6d2.
    • getDeviceIdB: generates a random ID in the format of UUID + timestamp with CRC8, such as Y8S5OQAAA2OPf0KLvixDxor2tEkkC8O4PZRZeMOiQSI=.
  • If you do not want to use the ANDROID_ID, use the provided fallback method.
  • Once the device token ID is generated, it is saved in the local storage. Next time the app is initiated, the saved device token ID is read first, unless the app is uninstalled.
copy
package com.iap.android.utilbox;

import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Base64;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

public class DeviceUtils {

    private static final String SP_STORAGE_FILE = "YOUR_SP_FILE";
    private static final String KEY_INSTANCE_ID = "YOUR_DEVICE_KEY";

    private static String deviceId;

    public static String getDeviceId(Context context) {
        if (!TextUtils.isEmpty(deviceId)) {
            return deviceId;
        }

        // get storage instanceId
        SharedPreferences sp = context.getSharedPreferences(
                SP_STORAGE_FILE, Context.MODE_PRIVATE);
        deviceId = sp.getString(KEY_INSTANCE_ID, "");
        if (!TextUtils.isEmpty(deviceId)) {
            return deviceId;
        }
        deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
        if (!TextUtils.isEmpty(deviceId)) {
            sp.edit().putString(KEY_INSTANCE_ID, deviceId).apply();
            return deviceId;
        }
        //getDeviceIdA();
        deviceId = getDeviceIdB();
        sp.edit().putString(KEY_INSTANCE_ID, deviceId).apply();

        return deviceId;
    }

    private static String getDeviceIdA() {
        return UUID.randomUUID().toString();
    }

    private static String getDeviceIdB() {
        return generateInstanceIdFromSeed(randomUUIDSeed());
    }

    private static byte[] randomUUIDSeed() {
        UUID first = UUID.randomUUID();
        UUID second = UUID.randomUUID();

        ByteBuffer buffer = ByteBuffer.wrap(new byte[32]);
        buffer.putLong(first.getMostSignificantBits());
        buffer.putLong(first.getLeastSignificantBits());

        buffer.putLong(second.getMostSignificantBits());
        buffer.putLong(second.getLeastSignificantBits());

        return buffer.array();
    }

    private static String generateInstanceIdFromSeed(byte[] bytes) {
        try {
            return generateInstanceIdInternal(bytes);
        } catch (Throwable t) {
        }

        return md5(bytes);
    }

    private static String generateInstanceIdInternal(byte[] bytes) throws Throwable {

        final int timestamp = (int) (System.currentTimeMillis() / 1000);
        final int random = (int) (Math.random() * 1000);

        ByteBuffer buffer = ByteBuffer.allocate(32);
        buffer.putInt(timestamp);
        buffer.putInt(random);

        if (bytes.length >= 23) {
            buffer.put(bytes, 0, 23);
        } else {
            buffer.put(bytes);
            buffer.put(new byte[23 - bytes.length]);
        }

        byte[] buildBytes = buffer.array();

        // calc crc-8
        Crc8 crc8 = new Crc8();
        crc8.update(buildBytes, 0, 31);
        buildBytes[31] = (byte) crc8.getValue();

        return Base64.encodeToString(buildBytes, Base64.NO_WRAP);
    }

    public static String md5(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return "";
        }
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            bytes = md5.digest(bytes);
            StringBuilder builder = new StringBuilder();

            for (byte b : bytes) {
                String temp = Integer.toHexString(b & 0xff);
                if (temp.length() == 1) {
                    builder.append('0');
                }

                builder.append(temp);
            }

            return builder.toString();
        } catch (NoSuchAlgorithmException e) {
        }
        return "";
    }

}
copy
public class Crc8 implements Checksum {

    private static final byte[] CRC8_TABLE = new byte[]{
            (byte) 0x00, (byte) 0x07, (byte) 0x0E, (byte) 0x09, (byte) 0x1C, (byte) 0x1B, (byte) 0x12, (byte) 0x15,
            (byte) 0x38, (byte) 0x3F, (byte) 0x36, (byte) 0x31, (byte) 0x24, (byte) 0x23, (byte) 0x2A, (byte) 0x2D,
            (byte) 0x70, (byte) 0x77, (byte) 0x7E, (byte) 0x79, (byte) 0x6C, (byte) 0x6B, (byte) 0x62, (byte) 0x65,
            (byte) 0x48, (byte) 0x4F, (byte) 0x46, (byte) 0x41, (byte) 0x54, (byte) 0x53, (byte) 0x5A, (byte) 0x5D,
            (byte) 0xE0, (byte) 0xE7, (byte) 0xEE, (byte) 0xE9, (byte) 0xFC, (byte) 0xFB, (byte) 0xF2, (byte) 0xF5,
            (byte) 0xD8, (byte) 0xDF, (byte) 0xD6, (byte) 0xD1, (byte) 0xC4, (byte) 0xC3, (byte) 0xCA, (byte) 0xCD,
            (byte) 0x90, (byte) 0x97, (byte) 0x9E, (byte) 0x99, (byte) 0x8C, (byte) 0x8B, (byte) 0x82, (byte) 0x85,
            (byte) 0xA8, (byte) 0xAF, (byte) 0xA6, (byte) 0xA1, (byte) 0xB4, (byte) 0xB3, (byte) 0xBA, (byte) 0xBD,
            (byte) 0xC7, (byte) 0xC0, (byte) 0xC9, (byte) 0xCE, (byte) 0xDB, (byte) 0xDC, (byte) 0xD5, (byte) 0xD2,
            (byte) 0xFF, (byte) 0xF8, (byte) 0xF1, (byte) 0xF6, (byte) 0xE3, (byte) 0xE4, (byte) 0xED, (byte) 0xEA,
            (byte) 0xB7, (byte) 0xB0, (byte) 0xB9, (byte) 0xBE, (byte) 0xAB, (byte) 0xAC, (byte) 0xA5, (byte) 0xA2,
            (byte) 0x8F, (byte) 0x88, (byte) 0x81, (byte) 0x86, (byte) 0x93, (byte) 0x94, (byte) 0x9D, (byte) 0x9A,
            (byte) 0x27, (byte) 0x20, (byte) 0x29, (byte) 0x2E, (byte) 0x3B, (byte) 0x3C, (byte) 0x35, (byte) 0x32,
            (byte) 0x1F, (byte) 0x18, (byte) 0x11, (byte) 0x16, (byte) 0x03, (byte) 0x04, (byte) 0x0D, (byte) 0x0A,
            (byte) 0x57, (byte) 0x50, (byte) 0x59, (byte) 0x5E, (byte) 0x4B, (byte) 0x4C, (byte) 0x45, (byte) 0x42,
            (byte) 0x6F, (byte) 0x68, (byte) 0x61, (byte) 0x66, (byte) 0x73, (byte) 0x74, (byte) 0x7D, (byte) 0x7A,
            (byte) 0x89, (byte) 0x8E, (byte) 0x87, (byte) 0x80, (byte) 0x95, (byte) 0x92, (byte) 0x9B, (byte) 0x9C,
            (byte) 0xB1, (byte) 0xB6, (byte) 0xBF, (byte) 0xB8, (byte) 0xAD, (byte) 0xAA, (byte) 0xA3, (byte) 0xA4,
            (byte) 0xF9, (byte) 0xFE, (byte) 0xF7, (byte) 0xF0, (byte) 0xE5, (byte) 0xE2, (byte) 0xEB, (byte) 0xEC,
            (byte) 0xC1, (byte) 0xC6, (byte) 0xCF, (byte) 0xC8, (byte) 0xDD, (byte) 0xDA, (byte) 0xD3, (byte) 0xD4,
            (byte) 0x69, (byte) 0x6E, (byte) 0x67, (byte) 0x60, (byte) 0x75, (byte) 0x72, (byte) 0x7B, (byte) 0x7C,
            (byte) 0x51, (byte) 0x56, (byte) 0x5F, (byte) 0x58, (byte) 0x4D, (byte) 0x4A, (byte) 0x43, (byte) 0x44,
            (byte) 0x19, (byte) 0x1E, (byte) 0x17, (byte) 0x10, (byte) 0x05, (byte) 0x02, (byte) 0x0B, (byte) 0x0C,
            (byte) 0x21, (byte) 0x26, (byte) 0x2F, (byte) 0x28, (byte) 0x3D, (byte) 0x3A, (byte) 0x33, (byte) 0x34,
            (byte) 0x4E, (byte) 0x49, (byte) 0x40, (byte) 0x47, (byte) 0x52, (byte) 0x55, (byte) 0x5C, (byte) 0x5B,
            (byte) 0x76, (byte) 0x71, (byte) 0x78, (byte) 0x7F, (byte) 0x6A, (byte) 0x6D, (byte) 0x64, (byte) 0x63,
            (byte) 0x3E, (byte) 0x39, (byte) 0x30, (byte) 0x37, (byte) 0x22, (byte) 0x25, (byte) 0x2C, (byte) 0x2B,
            (byte) 0x06, (byte) 0x01, (byte) 0x08, (byte) 0x0F, (byte) 0x1A, (byte) 0x1D, (byte) 0x14, (byte) 0x13,
            (byte) 0xAE, (byte) 0xA9, (byte) 0xA0, (byte) 0xA7, (byte) 0xB2, (byte) 0xB5, (byte) 0xBC, (byte) 0xBB,
            (byte) 0x96, (byte) 0x91, (byte) 0x98, (byte) 0x9F, (byte) 0x8A, (byte) 0x8D, (byte) 0x84, (byte) 0x83,
            (byte) 0xDE, (byte) 0xD9, (byte) 0xD0, (byte) 0xD7, (byte) 0xC2, (byte) 0xC5, (byte) 0xCC, (byte) 0xCB,
            (byte) 0xE6, (byte) 0xE1, (byte) 0xE8, (byte) 0xEF, (byte) 0xFA, (byte) 0xFD, (byte) 0xF4, (byte) 0xF3,
    };

    public static byte calcCrc8(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return 0;
        }

        Crc8 crc8 = new Crc8();
        crc8.update(bytes);

        return (byte) crc8.getValue();
    }

    private short value = 0;

    public Crc8() {
    }

    @Override
    public void update(byte[] buffer, int offset, int len) {
        if (buffer == null) {
            return;
        }

        final int max = offset + len;
        for (int i = offset; i < max; i++) {
            value = CRC8_TABLE[(value ^ buffer[i]) & 0xFF];
        }
    }

    public void update(byte[] buffer) {
        if (buffer == null) {
            return;
        }

        update(buffer, 0, buffer.length);
    }

    @Override
    public void update(int b) {
        value = CRC8_TABLE[(value ^ b) & 0xFF];
    }

    @Override
    public long getValue() {
        return value & (byte) 0xff;
    }

    @Override
    public void reset() {
        value = 0;
    }
}