# 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](https://idocs-assets.marmot-cloud.com/storage/idocs87c36dc8dac653c1/1687174635419-1823a42e-8ff5-470f-8307-f6f6b7db52f4.png)

Figure 1: Notify users of Alipay+ availability

1.  The MPP app starts the periodic task by calling the **startMonitor** API. See [Sample code](#NP0za) 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

```java
public interface IAlipayPlusRegionListener {
    void didEnterSupportedAlipayPlusRegion();
}
```

```java
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

```objectivec
@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
```

```objectivec
#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
```