Alipay+ DocsAlipay+ Docs

Notify users of Alipay+ availability

When a user travels abroad, the Mobile Payment Provider (MPP) can proactively notify the user of Alipay+ availability if you (the MPP) detect that the user is abroad and Alipay+ is available in the user's region.

This topic introduces how to check Alipay+ availability and send notifications when Alipay+ is available.

Workflow

The following figure illustrates the workflow of the periodic task which is to check whether the user is abroad in a location where Alipay+ is available and send notifications to users of Alipay+ availability.

见支可付.png

Figure 1: Notify users of Alipay+ availability

  1. The MPP app starts the periodic task by calling the startMonitor API. See Sample code for more information. (Step 1)
  2. The MPP app checks whether the number of attempts to check Alipay+ availability exceeds the daily limit. The MPP can set the maximum limit for a natural day. (Step 2)
  3. (Optional) If the attempt is within the maximum number of attempts, the MPP app can obtain the user's location based on its own processing logic or calls the getCurrentRegion API from Alipay+ Client SDK to obtain the user's location. (Steps 3-5)
  4. If the attempt is within the maximum number of attempts, the MPP app calls the isAlipayplusSupportedRegion API to check whether Alipay+ is available in the user's current location. (Steps 6-7)
  5. The registered listener in the startMonitor API notifies the MPP app when the user is abroad and in a region where the Alipay+ is available. Then, the MPP app decides the notification method, such as in-app notification or SMS message, and the frequency for notifying the user of Alipay+ availability. (Steps 8-9)

Sample code

Android sample code

copy
public interface IAlipayPlusRegionListener {
    void didEnterSupportedAlipayPlusRegion();
}
copy
public class AlipayPlusRegionHelper {
    private static volatile AlipayPlusRegionHelper instance;

    private final long crossBorderDetectedRefreshTime = 2 * 60 * 60 * 1000;
    private final int maxDetectedCount = 10;

    private Handler crossingBorderDetectHandler;

    private IAlipayPlusRegionListener listener;
    private boolean hasStartMonitor = false;
    public final String KEY_CURRENT_DETECTED_COUNT = "currentDetectedCount";
    public final String KEY_LAST_DETECTED_TIME = "lastDetectedTime";

    private final String MCC_STORAGE_NAME = "mccAlipayPlus";

    public static AlipayPlusRegionHelper getInstance() {
        if (instance == null) {
            synchronized (AlipayPlusRegionHelper.class) {
                if (instance == null) {
                    instance = new AlipayPlusRegionHelper();
                }
            }
        }
        return instance;
    }

    /**
     * start monitoring whether A+ is available and then notifying
     *
     * @param listener IAlipayPlusRegionListener
     */
    public synchronized void startMonitor(Context context, IAlipayPlusRegionListener listener) {
        if (hasStartMonitor) {
            return;
        }
        hasStartMonitor = true;

        this.listener = listener;
        if (crossingBorderDetectHandler == null) {
            HandlerThread thread = new HandlerThread("AlipayPlus-CrossingBorderDetected-Thread");
            thread.start();
            crossingBorderDetectHandler = new Handler(thread.getLooper());
        }

        //delay 6s ensuring getting latest regionRule
        scheduleNextDetectedTask(context, 6 * 1000);

    }

    /**
     * stop monitoring whether A+ is available and then notifying
     */
    public void stopMonitor() {
        hasStartMonitor = false;
        listener = null;
        if (crossingBorderDetectHandler != null) {
            crossingBorderDetectHandler.removeCallbacksAndMessages(null);
            crossingBorderDetectHandler = null;
        }
    }


    private void scheduleNextDetectedTask(Context context, long delayTimeMs) {
        if (context == null) {
            return;
        }

        crossingBorderDetectHandler.postDelayed(() -> {
            long lastRefreshTime = getLong(context, KEY_LAST_DETECTED_TIME, 0);
            long currentTime = System.currentTimeMillis();
            boolean isSameDay = isSameDate(lastRefreshTime, currentTime);

            if (!isSameDay) {
                putInt(context, KEY_CURRENT_DETECTED_COUNT, 0);
            }

            int currentMaxCount = getInt(context, KEY_CURRENT_DETECTED_COUNT, 0);

            if (currentMaxCount < maxDetectedCount) {
                currentMaxCount++;
                putInt(context, KEY_CURRENT_DETECTED_COUNT, currentMaxCount);
                putLong(context, KEY_LAST_DETECTED_TIME, System.currentTimeMillis());


                String region = AlipayPlusClient.getInstance().getCurrentRegion(context);
                boolean isAvailable = AlipayPlusClient.getInstance().isAlipayPlusSupportedRegion(context, region);
                if (isAvailable) {
                //notify bizPart crossing border and A+ supported
                if (listener != null) {
                //back to main thread
                Handler mainHandler = new Handler(Looper.getMainLooper());
                mainHandler.post(() -> listener.didEnterSupportedAlipayPlusRegion());
            }
            }
            }

                //prepare next task
                scheduleNextDetectedTask(context, crossBorderDetectedRefreshTime);
            }, delayTimeMs);
            }

                private boolean isSameDate(long time1, long time2) {
                boolean result = false;
                try {
                Calendar calendar1 = Calendar.getInstance();
                Calendar calendar2 = Calendar.getInstance();
                @SuppressLint("SimpleDateFormat") SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String dataStr1 = df.format(time1);
                String dataStr2 = df.format(time2);
                java.util.Date date1 = df.parse(dataStr1);
                java.util.Date date2 = df.parse(dataStr2);
                if (date1 != null && date2 != null) {
                calendar1.setTime(date1);
                calendar2.setTime(date2);

                result = (calendar1.get(Calendar.ERA) == calendar2.get(Calendar.ERA)) && (calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR)) && (calendar1.get(Calendar.DAY_OF_YEAR) == calendar2.get(Calendar.DAY_OF_YEAR));
            }
            } catch (Exception ignored) {

            }
                return result;
            }

                private void putLong(Context context, String key, long value) {
                context.getSharedPreferences(MCC_STORAGE_NAME, Context.MODE_PRIVATE).edit().putLong(key, value).apply();
            }

                private Long getLong(Context context, String key, long defaultValue) {
                return context.getSharedPreferences(MCC_STORAGE_NAME, Context.MODE_PRIVATE).getLong(key, defaultValue);
            }

                private void putInt(Context context, String key, int value) {
                context.getSharedPreferences(MCC_STORAGE_NAME, Context.MODE_PRIVATE).edit().putInt(key, value).apply();
            }

                private int getInt(Context context, String key, int defaultValue) {
                return context.getSharedPreferences(MCC_STORAGE_NAME, Context.MODE_PRIVATE).getInt(key, defaultValue);
            }
            }

iOS sample code

copy
@protocol IAlipayPlusRegionListenerProtocol <NSObject>

- (void)didEnterSupportedAlipayPlusRegion;

@end

@interface AlipayPlusRegionHelper : NSObject

+ (instancetype)shared;


/// start monitoring whether A+ is available and then notifying
/// - Parameter listenter: IAlipayPlusRegionListenerProtocol
- (void)startMonitor:(id<IAlipayPlusRegionListenerProtocol>)listenter;

/// stop monitoring whether A+ is available
- (void)stopMonitor;
@end
copy
#import "AlipayPlusRegionHelper.h"
#import <MPPAlipayPlusClient/MPPAlipayPlusClient.h>

@interface AlipayPlusRegionHelper ()
@property (nonatomic, strong) NSTimer *detectedTimer;
@property (nonatomic, strong, nullable) id<IAlipayPlusRegionListenerProtocol> listenter;
@end

@implementation AlipayPlusRegionHelper

NSString * const maxDetectedCount = @"maxDetectedCount";
NSString * const lastDetectedCount = @"lastDetectedCount";

NSInteger const MAX_DETECTED_COUNT = 10;
NSInteger const DETECTED_REFRESH_TIME = 2 * 60 * 60;

+ (instancetype)shared {
    static AlipayPlusRegionHelper *helper = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        helper = [AlipayPlusRegionHelper new];
    });
    return helper;
}


#pragma mark start monitoring whether A+ is available and then notifying
- (void)startMonitor:(id<IAlipayPlusRegionListenerProtocol>)listenter {
    if (listenter == nil || _detectedTimer.isValid) {
        return;
    }
    _listenter = listenter;
    __weak typeof(self) weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [weakSelf scheduledDetectedTask];
        weakSelf.detectedTimer = [NSTimer timerWithTimeInterval:DETECTED_REFRESH_TIME target:self selector:@selector(scheduledDetectedTask) userInfo:nil repeats:YES];
        [[NSRunLoop mainRunLoop] addTimer:weakSelf.detectedTimer forMode:NSRunLoopCommonModes];
    });
}

#pragma mark stop monitoring whether A+ is available
- (void)stopMonitor {
    [_detectedTimer invalidate];
    _detectedTimer = nil;
    _listenter = nil;
}


- (void)scheduledDetectedTask {
    NSDate *lastDate = [[NSUserDefaults standardUserDefaults] objectForKey:lastDetectedCount];
    bool isSameDay = [self isSameDay:lastDate date2:[NSDate date]];
    if (!isSameDay) {
        [[NSUserDefaults standardUserDefaults] setInteger:0 forKey:maxDetectedCount];
    }
    NSInteger currentCount = [[NSUserDefaults standardUserDefaults] integerForKey:maxDetectedCount];
    if (currentCount < MAX_DETECTED_COUNT) {
        currentCount++;
        [[NSUserDefaults standardUserDefaults] setInteger:currentCount forKey:maxDetectedCount];
        [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:lastDetectedCount];
        NSString *region = [MPPAlipayPlusClient.shared getCurrentRegion];
        bool isAvailable = [MPPAlipayPlusClient.shared isAlipayPlusSupportedRegion:region];
        if (isAvailable && self.listenter != nil) {
            if ([self.listenter respondsToSelector:@selector(didEnterSupportedAlipayPlusRegion)]) {
                [self.listenter didEnterSupportedAlipayPlusRegion];
            }
        }
    }
}

- (BOOL)isAlipayPlusAvailable:(NSString *)currentValue localValue:(NSArray *)localValue supportedAlipayPlusValue:(NSArray *)supportedAlipayPlusValue{
    if (currentValue.length == 0 || localValue.count == 0 || supportedAlipayPlusValue.count == 0) {
        return FALSE;
    }
    
    for (NSString *item in localValue) {
        if ([currentValue.uppercaseString isEqualToString:item.uppercaseString]) {
            return FALSE;
        }
    }
    
    for (NSString *item in supportedAlipayPlusValue) {
        if ([currentValue.uppercaseString isEqualToString:item.uppercaseString]) {
            return TRUE;
        }
    }
    return FALSE;
}

- (BOOL)isSameDay:(NSDate*)date1 date2:(NSDate*)date2 {
    if (date1 == nil || date2 == nil) {
        return false;
    }
    NSCalendar* calendar = [NSCalendar currentCalendar];
    
    unsigned unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth |  NSCalendarUnitDay;
    NSDateComponents* comp1 = [calendar components:unitFlags fromDate:date1];
    NSDateComponents* comp2 = [calendar components:unitFlags fromDate:date2];
    
    return [comp1 day]   == [comp2 day] &&
    [comp1 month] == [comp2 month] &&
    [comp1 year]  == [comp2 year];
}

@end