# 开发者常见问题

# 1. 打开vsconsole的方法

app.json文件加入配置项"debug": true

# 2. 自定义组件json文件

为保证自定义组件正常渲染,组件json文件需声明"component": true

# 3. 导航栏配置navigationStyle

该属性在小程序中可以设置三个值: default/custom/hide

  • default:默认样式
  • custom:隐藏导航栏
  • hide:隐藏胶囊

# 4. 暂不支持的用户登录等接口

  • ft.checkSession
  • ft.getAccountInfoSync
  • UserInfo
  • ft.authorize

# 5. wxss引用base64格式图片失败

没有遵循 base64标准,如:'data:'后有多余空格

# 6. 如何将H5工程转为小程序

即:在小程序中嵌入H5页面方法

  1. 小程序实现:
    使用web-view组件,在src属性中写入H5页面url即可

        <web-view src="www.baidu.com"></web-view>
    

    也可以使用数据绑定方式

        <web-view :src="{{webViewUrl}}"></web-view>
    
  2. 在本地磁盘中找到对应项目,将项目文件压缩为zip包

  3. 上传小程序代码包
    在【开发设置-代码包管理】中,上传该代码包

  4. 完成域名配置
    在【开发设置-域名管理】中,完成对应域名请求设置

  5. 通过 FinClip APP 或已集成SDK的APP查看小程序

# 7. transitionend 事件

目前不支持 transitionend 事件,请开发者注意避免使用。

# 8. FinClip小程序可以在支付宝或字节的开发工具上开发么?

可以。

但目前FinClip与微信小程序兼容最佳,基于微信开发的小程序可在FinClip平台中完成一键上传、直接使用,运行效果与微信基本无异。

如果开发者是在支付宝或字节上直接开发的原生小程序,可参考:

  • 使用支付宝或字节提供的工具,将小程序直接转换成微信小程序后,再将转换后的代码包上传FinClip,此时即可直接使用;
  • 开发者亦可自行手动替换,即:将文件后缀及API命名空间替换为 'ft.xxx' ;

如果开发者在小程序开发时,使用了支持多平台的第三方小程序框架,此类框架在FinClip上也可直接运行。

当前,FinClip兼容性较好的第三方框架包括:

除以上所述,如小程序中使用了某些平台的定制API,亦将导致小程序上传FinClip后,功能无法正常运行。此类定制API主要为支付、登录等与原生App业务息息相关的接口。

开发者在上传代码包时,应提前处理。

# 9. FinClip小程序中如何对接微信登录?FinClip小程序如何接入APP的授权登录?

通常来说,真正意义的微信小程序授权登录只能在微信的APP中进行,是指由微信APP授权给微信小程序;而FinClip小程序的授权登录则是通过集成了SDK的第三方APP进行授权(因为一般APP自己就具有账号体系)。

所以,即使第三方APP支持通过微信账号体系进行登录,然后再授权给FinClip小程序进行登录,也不能算真正意义的微信授权登录,本质上是第三方APP对FinClip小程序的授权登录。

综上,如果想实现APP对FinClip小程序的授权登录,具体可参考如下步骤:

# 9.1 APP接入微信登录

若您需要实现APP的微信账号授权登录,请参考微信开放平台的移动应用开发 (opens new window)微信授权登录开发文档 (opens new window)进行实现,若不需要则直接从第2步开始即可; ​

# 9.2 APP实现自定义注入接口

首先,APP需要参考iOS SDK自定义注入接口Android SDK自定义注入接口两份文档,将账号的授权登录能力注入至SDK中,让小程序能调用。详细说明如下: ​

Android端

为了让小程序能够获取到小程序以外的APP数据,需要注册小程序自定义接口,自定义小程序接口具体说明请参照FinClip小程序开放平台-自定义小程序接口FinClip小程序开放平台-在小程序进程中注册api

  1. 自定义login接口
    自定义Api类,指定api名称为“login”;
public class LoginApi extends AbsApi {

    private final static String API_NAME_LOGIN = "login"; // 小程序基础库调用的api名称

    @Override
    public String[] apis() {
        return new String[]{API_NAME_LOGIN};
    }
}
  1. 注册自定义Api;
    示例展示了用户授权提示Dialog,需要Activity对象作为Dialog的context参数, 所以需要在小程进程注册自定义Api。
  if (FinAppClient.INSTANCE.isFinAppProcess(this)) {
      // 小程序进程
      // 小程序进程中注册api的方法能获取到小程序所在activity对象,可以用做创建对话框的context参数)
      FinAppProcessClient.INSTANCE.setCallback(new FinAppProcessClient.Callback() {
          @Override
          public List<IApi> getRegisterExtensionApis(@NotNull Activity activity) {
              ArrayList<IApi> apis = new ArrayList<>();
              apis.add(new LoginApi());
              return apis;
          }

          @Nullable
          @Override
          public List<IApi> getRegisterExtensionWebApis(@NotNull Activity activity) {
              return null;
          }
      });
      return;
  }
  1. 实现自定义Api invoke()方法;
    先展示授权提示Dialog(开发者请根据需求决定是否展示授权提示Dialog), 然后再从主进程(App进程)获取用户登录信息。
public class LoginApi extends AbsApi {

    @Override
    public void invoke(String event, JSONObject param, ICallback callback) {
        if (event.equals(API_NAME_LOGIN)) {
            showAuthDialog(callback);
        }
    }

    /**
     * 显示获取用户登录信息的授权提示对话框
     */
    private void showAuthDialog(final ICallback callback) {
        // 是否需要显示授权提示对话框请开发者按照产品需求自行处理
        new AlertDialog.Builder(activity)
                .setTitle("是否同意授权获取用户登录信息?")
                .setNegativeButton("拒绝", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        callback.onFail();
                    }
                })
                .setPositiveButton("同意", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        loginMainProcess(callback);
                    }
                })
                .show();
    }

    /**
     * 从主进程获取用户登录信息
     */
    private void loginMainProcess(final ICallback callback) {
        // 小程序进程调用主进程,在主进程获取用户信息后返回给小程序进程
        FinAppProcessClient.INSTANCE.getAppletProcessApiManager()
                .callInMainProcess(API_NAME_LOGIN, null, new FinCallback<String>() {
                    @Override
                    public void onSuccess(final String result) {
                        // 需要在主线程调用callback方法
                        activity.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    callback.onSuccess(new JSONObject(result));
                                } catch (JSONException e) {
                                    e.printStackTrace();
                                    callback.onFail();
                                }
                            }
                        });
                    }

                    @Override
                    public void onError(int code, String error) {
                        callback.onFail();
                    }

                    @Override
                    public void onProgress(int status, String info) {
                    }
                });
    }

}

随后在主进程(通常是在Application里面,开发者也可以选择其他合适的位置), 返回用户登录信息给小程序进程。

// 在主进程设置"小程序进程调用主进程"的处理方法
// 开发者也可以选择在主进程其他合适的代码位置设置处理方法
FinAppClient.INSTANCE.getAppletApiManager()
    .setAppletProcessCallHandler(new IAppletApiManager.AppletProcessCallHandler() {
        @Override
        public void onAppletProcessCall(@NotNull String name,
                                        @Nullable String params,
                                        @Nullable FinCallback<String> callback) {
            if (callback != null) {
                if (name.equals(LoginApi.API_NAME_LOGIN)) {
                    // 从主进程获取登录信息,返回给小程序进程
                    // 这里返回的是虚拟的用户登录信息,开发者请从APP里面自行获取用户登录信息
                    JSONObject jsonObject = new JSONObject();
                    try {
                        jsonObject.put("userId", "123");
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    callback.onSuccess(jsonObject.toString());
                }
            }
        }
    });

至此,小程序通过自定义Api从APP获取用户登录信息的整个流程就已经完成了。 ​

注意

如果产品需求不需要展示用户授权提示Dialog,建议在主进程(APP进程)注册自定义Api,从而省掉上述在小程序进程调用主进程(APP进程)方法获取数据的过程。

  1. 自定义getUserProfile接口

    1. 自定义getUserProfile接口的api名称为“getUserProfile”。
    2. 自定义getUserProfile接口流程与自定义login接口流程一致,此处不再赘述。
  2. FinClip还提供了官方的Android APP Demo 工程和小程序 (opens new window)作为示范,如有需求,您可以前往下载体验。

iOS 端

  1. 在原生App中调用SDK的注入login方法,login是微信小程序对应的登录方法名。
[[FATClient sharedClient] registerExtensionApi:@"login" handle:^(id param, FATExtensionApiCallback callback) {
    // 处理小程序登录逻辑(比如微信登录或其他方式的登录)后,调用小程序回调
    // 登录成功回调示例
    callback(FATExtensionCodeSuccess,@{@"desc":@"登录成功"});
}];
  1. 在原生App中调用SDK的注入getUserProfile方法,getUserProfile是微信小程序对应的获取用户信息方法名。
[[FATClient sharedClient] registerExtensionApi:@"getUserProfile" handle:^(id param, FATExtensionApiCallback callback) {
    // 获取app上的用户信息(可以是微信登录或者其他登录的用户信息),然后通过回调给到小程序使用
    NSDictionary *userInfo = @{@"nickName":@"张三",@"avatarUrl":@"",@"gender":@1,@"country":@"中国",@"province":@"广东省",@"city":@"深圳",@"language":@"zh_CN"};
    NSDictionary *resDic = @{@"userInfo":userInfo};
    callback(FATExtensionCodeSuccess,resDic);
}];
  1. 在原生App中调用SDK的注入requestPayment方法,requestPayment是微信小程序对应的支付方法名。
__weak typeof(self) weakSelf = self;
[[FATClient sharedClient] registerExtensionApi:@"requestPayment" handle:^(id param, FATExtensionApiCallback callback) {
    // 支付调用,调用结果通过回调通告小程序
    [weakSelf getTestPayment:callback];
}];

当前类持有requestPayment api的回调

// 支付时,支付的回调是异步的,故这里将小程序的回调持有,在适当的时机调用(用户可根据自己的场景自行实现自己调用的逻辑)
@property (nonatomic, copy) FATExtensionApiCallback callback;

模拟支付

- (void)getTestPayment:(FATExtensionApiCallback)callback {
    NSString *urlString = @"https://xxx.xxx.com/api/order"; // 下单接口,模拟下单
    NSURL *url = [NSURL URLWithString:urlString];
    NSMutableURLRequest *requset = [NSMutableURLRequest requestWithURL:url];
    requset.HTTPMethod = @"POST";
    __weak typeof(self) weakSelf = self;
    NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:requset completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        if (httpResponse.statusCode == 200) {
            // 下单成功后,模拟微信支付
            PayReq *request = [[PayReq alloc] init];
            request.partnerId = @"partnerId";
            request.prepayId = @"prepayId";
            request.package = @"Sign=WXPay";
            request.nonceStr = @"nonceStr";
            request.timeStamp = 100000;
            request.sign= @"sign";
            dispatch_async(dispatch_get_main_queue(), ^{
                [WXApi sendReq:request completion:^(BOOL success) {
                    if (success) {
                        // 唤起微信成功时,将回调持有,方便后面使用(这里实现方式可以自行定义,此处只是模拟)
                        weakSelf.callback = callback;
                    } else {
                        // 唤起微信失败时,直接调用回调,传入失败
                        callback(FATExtensionCodeFailure, nil);
                    }
                }];
            });
            return;
        }
        callback(FATExtensionCodeFailure, nil);
    }];
    [task resume];
}

微信支付成功时,回调

#pragma mark - WXApiDelegate
//发起支付请求回调
- (void)onReq:(BaseReq *)req {
    
}

//支付结果回调
- (void)onResp:(BaseResp *)resp {
    if ([resp isKindOfClass:[PayResp class]]) {
        PayResp *response = (PayResp*)resp;
        //response.errCode
//        WXSuccess           = 0,    /**< 成功    */
//        WXErrCodeCommon     = -1,   /**< 普通错误类型    */
//        WXErrCodeUserCancel = -2,   /**< 用户点击取消并返回    */
//        WXErrCodeSentFail   = -3,   /**< 发送失败    */
//        WXErrCodeAuthDeny   = -4,   /**< 授权失败    */
//        WXErrCodeUnsupport  = -5,   /**< 微信不支持    */
        switch (response.errCode) {
            case WXSuccess: // 支付成功时,回调调用
                self.callback(FATExtensionCodeSuccess, nil);
                break;
                
            default:    // 支付失败时,回调调用
                self.callback(FATExtensionCodeFailure, nil);
                break;
        }
    }
}

注意

小程序支付api requestPayment的回调,需要用户进行维护。因为支付功能是异步的,需要在支付成功或失败的回调中,调用小程序支付api requestPayment的回调。 这里伪代码使用当前类持有小程序支付api requestPayment的回调,在支付功能的回调中进行调用。用户可以根据自己的场景需求,使用其他技术进行处理。

  1. FinClip还提供了官方的iOS APP Demo 工程和小程序 (opens new window)作为示范,您可以前往下载体验。

# 9.3 小程序实现接口的调用

​ 小程序如需支持 login、getUserProfile 和 requestPayment api,需要在 APP  以注入 API 的方式实现,注入 API 的实现步骤可参阅第2步。​

需要注意的是,login、getUserProfile 和 requestPayment 为小程序默认注入接口,无需在小程序根目录配置 FinChatConf 即可调通该两个接口。​

当 APP 未注入相关接口时,调用会报错并执行 fail 回调。 ​

  1. 调起登录接口
wx.login({
  param1: '', // 按需添加参数,会透传给 APP 处理
  success: (res) => {
    console.log('login success', res)
  },
  fail: (res) => {
    console.log('login fail', res.errMsg)
  }
})
  1. 调起获取用户信息接口
wx.getUserProfile({
  success: (res) => {
    console.log('getUserProfile success', res)
  },
  fail: (res) => {
    console.log('getUserProfile fail', res.errMsg)
  }
})
  1. 调起支付接口
wx.requestPayment({
  success: (res) => {
    console.log('requestPayment success', res)
  },
  fail: (res) => {
    console.log('requestPayment fail', res.errMsg)
  } 
})

# 10. FinClip 小程序中如何实现第三方登录?

您可以点击下面链接查看在 iOS 与 Android 中的相关设置方法:

# 11. 在FinClip小程序中如何对接微信支付?

由于FinClip小程序是运行在微信以外的集成了FinClip SDK 的第三方APP,故不能使用原来在微信小程序中已经实现的微信支付;而是先让第三方APP接入微信支付的能力,然后再由小程序调起APP的微信支付能力,最后跳转至微信完成支付。具体实现步骤如下:
image.png

# 11.1 APP接入微信支付

首先APP需要接入微信支付能力:微信支付支持在开放平台 (opens new window)注册并完成认证的移动端应用APP接入支付功能。APP接入支付后,商户通过微信提供的SDK调用微信支付模块完成收款需求。
目前微信支付支持手机系统有:IOS(苹果)、Android(安卓)和WP(Windows Phone)。详情说明请参考微信支付APP接入介绍 (opens new window)指引文档 (opens new window)

# 11.2 APP实现自定义注入接口

APP集成了微信支付的能力后,需要开发者注册自定义接口将支付能力注入至SDK中,让小程序能调用,详细说明如下。

# Android端

注册自定义小程序接口的具体说明请参照FinClip小程序开放平台-自定义小程序接口 (opens new window)

  1. 自定义Api类,实现AbsApi接口,指定api名称为"requestPayment"。
public class WxPayApi extends AbsApi {

    private final Context context;

    public WxPayApi(Context context) {
        this.context = context;
    }

    private static final String API_NAME_REQUEST_PAYMENT = "requestPayment";

    @Override
    public String[] apis() {
        return new String[]{API_NAME_REQUEST_PAYMENT};
    }
    
    // ...
}


  1. 注册自定义Api类。
if (FinAppClient.INSTANCE.isFinAppProcess(this)) {
	return;
}

// SDK初始化结果回调,用于接收SDK初始化状态
FinCallback<Object> callback = new FinCallback<Object>() {
    @Override
    public void onSuccess(Object result) {
        // SDK初始化成功,注册自定义小程序接口
        FinAppClient.INSTANCE.getExtensionApiManager().registerApi(new WxPayApi(DemoApp.this));
    }

    // ...
};


  1. 实现自定义Api类invoke()方法,在invoke方法内调用后台下单接口,下单成功后调起微信支付。
@Override
public void invoke(String event, JSONObject param, ICallback callback) {
    if (API_NAME_REQUEST_PAYMENT.equals(event)) {
        IWXAPI wxApi = WXAPIFactory.createWXAPI(context, null);
        // 判断是否安装了微信
        if (!wxApi.isWXAppInstalled()) {
            JSONObject result = new JSONObject();
            try {
                result.put("errMsg", "WeChat not installed");
            } catch (JSONException e) {
                e.printStackTrace();
            }
            callback.onFail(result);
            return;
        }

        // 调用后台下单接口,拿到调起微信支付所需参数,此处省略,开发者请自行实现
        // ...

        PayReq request = new PayReq();
        request.appId = Constants.WX_APP_ID;
        request.partnerId = mchid;
        request.prepayId = prepayId;
        request.packageValue = "Sign=WXPay"; // 微信订单详情扩展字符串,暂填写固定值Sign=WXPay
        request.nonceStr = nonceStr; // 随机字符串,不长于32位
        request.timeStamp = timeStamp;
        request.sign = sign;
        wxApi.sendReq(request); // 调起微信支付
    }
}


  1. 在微信支付信息回调WXPayEntryActivity,结果回调方法内利用自定义api invoke方法参数callBack对象将支付结果返回给小程序。
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {

    // ...

    @Override
    public void onResp(BaseResp resp) {
        if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
            WxPayApi.notifyPayResult(resp.errCode);
            finish();
        }
    }

}

public class WxPayApi extends AbsApi {

    // ...

    private static ICallback callback;

    public static void notifyPayResult(int errCode) {
        if (callback != null) {
            if (errCode == 0) {
                callback.onSuccess(null);
            } else {
                JSONObject result = new JSONObject();
                try {
                    result.put("errCode", errCode);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                callback.onFail(result);
            }
            WxPayApi.callback = null;
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        callback = null;
    }

    @Override
    public void invoke(String event, JSONObject param, ICallback callback) {
        if (API_NAME_REQUEST_PAYMENT.equals(event)) {
            WxPayApi.callback = null;

            // ...
        }
    } 
}


  1. FinClip还提供了官方的安卓APP Demo 工程和小程序 (opens new window)作为示范,如有需求,您可以前往下载体验。

# iOS 端

  1. 在原生App中调用SDK的注入requestPayment方法,requestPayment是微信小程序对应的支付方法名。
__weak typeof(self) weakSelf = self;
[[FATClient sharedClient] registerExtensionApi:@"requestPayment" handle:^(id param, FATExtensionApiCallback callback) {
    // 下单-支付调用,调用结果通过回调通告小程序
    [weakSelf getTestPayment:callback];
}];
  1. 模拟下单、支付
// 支付时,支付的回调是异步的,故这里将小程序的回调持有,在适当的时机调用(用户可根据自己的场景自行实现自己调用的逻辑)
@property (nonatomic, copy) FATExtensionApiCallback callback;
- (void)getTestPayment:(FATExtensionApiCallback)callback {
    NSString *urlString = @"https://xxx.xxx.com/api/order"; // 下单接口,模拟下单
    NSURL *url = [NSURL URLWithString:urlString];
    NSMutableURLRequest *requset = [NSMutableURLRequest requestWithURL:url];
    requset.HTTPMethod = @"POST";
    __weak typeof(self) weakSelf = self;
    NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:requset completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        if (httpResponse.statusCode == 200) {
            // 下单成功后,模拟微信支付
            PayReq *request = [[PayReq alloc] init];
            request.partnerId = @"partnerId";
            request.prepayId = @"prepayId";
            request.package = @"Sign=WXPay";
            request.nonceStr = @"nonceStr";
            request.timeStamp = 100000;
            request.sign= @"sign";
            dispatch_async(dispatch_get_main_queue(), ^{
                [WXApi sendReq:request completion:^(BOOL success) {
                    if (success) {
                        // 唤起微信成功时,将回调持有,方便后面使用(这里实现方式可以自行定义,此处只是模拟)
                        weakSelf.callback = callback;
                    } else {
                        // 唤起微信失败时,直接调用回调,传入失败
                        callback(FATExtensionCodeFailure, nil);
                    }
                }];
            });
            return;
        }
        callback(FATExtensionCodeFailure, nil);
    }];
    [task resume];
}
  1. 微信支付成功时,回调处理
#pragma mark - WXApiDelegate
//发起支付请求回调
- (void)onReq:(BaseReq *)req {
    
}

//支付结果回调
- (void)onResp:(BaseResp *)resp {
    if ([resp isKindOfClass:[PayResp class]]) {
        PayResp *response = (PayResp*)resp;
        //response.errCode
//        WXSuccess           = 0,    /**< 成功    */
//        WXErrCodeCommon     = -1,   /**< 普通错误类型    */
//        WXErrCodeUserCancel = -2,   /**< 用户点击取消并返回    */
//        WXErrCodeSentFail   = -3,   /**< 发送失败    */
//        WXErrCodeAuthDeny   = -4,   /**< 授权失败    */
//        WXErrCodeUnsupport  = -5,   /**< 微信不支持    */
        switch (response.errCode) {
            case WXSuccess: // 支付成功时,回调调用
                self.callback(FATExtensionCodeSuccess, nil);
                break;
                
            default:    // 支付失败时,回调调用
                self.callback(FATExtensionCodeFailure, nil);
                break;
        }
    }
}

注意:
小程序支付api requestPayment的回调,需要用户进行维护。因为支付功能是异步的,需要在支付成功或失败的回调中,调用小程序支付api requestPayment的回调。
这里伪代码使用当前类持有小程序支付api requestPayment的回调,在支付功能的回调中进行调用。用户可以根据自己的场景需求,使用其他技术进行处理。

  1. FinClip还提供了官方的iOS APP Demo 工程和小程序 (opens new window)作为示范,您可以前往下载体验。

# 11.3 小程序实现接口的调用

移动端相关 api 准备好后,在小程序中使用代码如下:

// 小程序中调用代码
wx.requestPayment({
  success: (res) => {
    console.log('payment success', res)
  },
  fail: (res) => {
    console.log('payment fail', res.errMsg)
  }
})

调用 api 成功且支付成功后

# 11.4 小程序接入支付宝支付

FinClip小程序接入支付宝支付的步骤与接入微信支付的步骤基本一致,此处不再赘述,APP接入支付宝支付请参考文档支付宝支付接入文档 (opens new window)

© 2021 凡泰极客
  • 免费试用
  • 编组
  • 编组 2