快速接入
一、交互流程
1.1 免登录接口
需要您后端服务进行API
接口对接(详见《接入指引》),接口调用完成后会返回fullUrl
,直接访问 fullUrl
即可访问短剧宝对应服务。您可以采用重定向跳转或webview
嵌入等技术手段进行嵌入,具体接入可参考《二、短剧宝H5嵌入最佳实践》
sequenceDiagram
商户平台->>短剧宝API: 1、调用免登录接口
短剧宝API-->>商户平台:2、返回 fullUrl
商户平台-) 短剧宝H5服务:3、 访问 fullUrl
1.2 其他接口
需要您后端服务进行API接口对接(详见《接入指引》),同时参考各接口的使用场景和注意事项配合商户平台活动使用。
注意事项:解锁指定短剧、开通VIP会员的前提需要用户已注册存在,否则无法解锁短剧、开通会员
1.2.1 解锁指定短剧接口
sequenceDiagram
商户平台->>短剧宝API: 1、调用解锁指定短剧接口
短剧宝API-->>商户平台:2、返回解锁短剧订单号orderNo和订单状态orderStatus:UNLOCK_SUCCESS
商户平台-) 短剧宝H5服务:3、我的订单查看解锁短剧订单、剧集详情查看解锁剧集全集
1.2.2 开通VIP会员接口
sequenceDiagram
商户平台->>短剧宝API: 1、调用开通VIP会员接口
短剧宝API-->>商户平台:2、返回解锁会员订单号orderNo和订单状态orderStatus:UNLOCK_SUCCESS
商户平台-) 短剧宝H5服务:3、我的订单查看解锁会员订单、我的会员卡片为开通状态、推广会员海报可分享
1.2.3 查询剧列表
sequenceDiagram
商户平台->>短剧宝API: 1、调用查询剧列表接口
短剧宝API-->>商户平台:2、返回对应商户的剧列表
1.2.4 查询订单
sequenceDiagram
商户平台->>短剧宝API: 1、调用查询订单接口
短剧宝API-->>商户平台:2、返回订单信息
二、短剧宝H5嵌入最佳实践
2.1、webview
嵌入模式
uniapp
中内嵌短剧宝,html,body
不要设置height:unset
; 否则可能出现嵌入后小剧场上下按钮无法点击情况web-view
不需要加任何样式,直接拉起fullUrl
即可;**同时建议在嵌入页面添加加载动画效果,且对于加载失败的下拉刷新效果,这样对于一些弱网环境用户体验更佳**
如果关注短剧宝分享的信息需要在在免密登录接口中添加参数:shareDjbPath、parentPhone
- shareDjbPath 是短剧宝分享页面携带的参数,需要在短剧宝宿主开始时进行保存
- parentPhone 在 shareDjbPath 字段中提取
则参数:
- shareDjbPath 为: /pages/shortPlay/playPage?seriesId=79&ts=1715246855974&userId=8643618384667959296&userName=185****3453&parentPhone=18511253453
- parentPhone 为: 18511253453
进入宿主页面如果未登录,最好先存一下 shareDjbPath 到本地缓存,并且在登录过后发现有 shareDjbPath 的时候直接跳到短剧宝宿主页面,否则shareDjbPath将丢掉,不能构建短剧宝所属关系
如返回地址为:
代码示例:
<template>
<view class="estore">
<web-view v-if="!loading" :src="iframeUrl"></web-view>
<view class="load-wrap" v-show="loading">
<image class="image" src="@/static/images/loading.gif" mode=""></image>
<view class="load-text">拼命加载中…</view>
</view>
</view>
</template>
<script>
import httpLogin from '@/api/login'
import {isWeixin} from '@/utils/verify'
const app = getApp()
export default {
data() {
return {
loading: true,
token: uni.getStorageSync("token"),
userId: uni.getStorageSync("user_id"),
iframeUrl: '',
shareDjbPath: ''
};
},
onLoad() {
let params = new URLSearchParams(location.search),
insetUrl = params.get('insetUrl');
/**
* onLaunch(e) {
* // 首次没有登录时需要把 shareDjbPath 存到缓存中
* if (e.query.shareDjbPath) {
* uni.setStorageSync('shareDjbPath', e.query.shareDjbPath);
* }
* }
*/
// shareDjbPath 判断是否有跳转链接,url 中没有就取缓存中的
this.shareDjbPath = params.get('shareDjbPath') || uni.getStorageSync('shareDjbPath');
// history.replaceState(null, '', location.origin + location.pathname);
if (insetUrl) {
this.iframeUrl = insetUrl;
this.loading = false;
return;
}
this.validateToken();
},
onBackPress(options) {
if (options.from === 'backbutton') {
uni.switchTab({
url: '/pages/index/index'
})
return true;
}
},
mounted() {
window.history.pushState(null, null, '#')
window.addEventListener("popstate", function (e) {
uni.switchTab({
url: '/pages/index/index'
})
}, false);
if (isWeixin()) {
window.addEventListener('message', (e) => {
if (e.origin && e.data === 'WeixinJSBridge') {
if (typeof WeixinJSBridge !== 'undefined') {
WeixinJSBridge.invoke("getNetworkType", {}, () => {
let ele = document.getElementsByTagName('iframe')[0];
ele.contentWindow.postMessage('WeixinJSBridge', '*');
});
}
}
}, false);
}
},
methods: {
// 校验用户token 是否有效
validateToken() {
app._get("user.index/detail", {}, (result) => {
if (1 == result.code && result.data.userInfo) {
this.loginShotVideo(result.data.userInfo.user_id)
// 如果是登录状态,删除 路径缓存
uni.removeStorageSync('shareDjbPath');
} else {
uni.removeStorageSync('token');
uni.redirectTo({url: '/pages/login/login'});
}
});
},
// 登陆 estore
loginShotVideo(userId) {
let params = {
token: this.token,
userId: userId || this.userId,
source: 'h5',
thirdType: 'DJB',
shareDjbPath: this.shareDjbPath,
redirectUrl: encodeURIComponent(location.origin + location.pathname),
};
if (this.shareDjbPath) {
let paramsEms = new URLSearchParams(this.shareDjbPath.slice(this.shareDjbPath.indexOf('?')));
params.parentPhone = paramsEms.get('parentPhone');
}
// 免密登录请求
httpLogin.loginDJB(params).then(result => {
if (result.code == 1 && result.data && result.data.url) {
this.loading = false;
let iframeUrl = result.data.url;
this.iframeUrl = iframeUrl;
this.shareDjbPath = '';
} else {
uni.showToast({
title: result.message || '登陆失败,请稍后重试~',
icon: 'none'
})
setTimeout(() => {
uni.redirectTo({
url: '/pages/login/login'
});
}, 1500)
}
})
}
}
}
</script>
解决支付回调问题,进入页面 uniapp onLoad 中或 页面开始时增加代码
// 获取当前路由的 search let params = new URLSearchParams(location.search), insetUrl = params.get('insetUrl'); // 判断 search 中是否有 insetUrl 参数, 如果就不要接口获取 webview 的 URL 了 if (insetUrl) { // 如果有将 webview 的 URL 设置为 insetUrl this.iframeUrl = insetUrl; // 你的页面的路径 let baseUrl = location.origin + location.pathname; // 将路径中的insetUrl删除 history.replaceState(null, '', baseUrl); }
如果需要支持微信浏览器打开短剧宝,需要页面可执行位置中加如下代码
// 判断是否微信浏览器 let ua = window.navigator.userAgent.toLowerCase(); if (ua.match(/MicroMessenger/i) === 'micromessenger') { window.addEventListener('message', (e) => { if (e.origin && e.data === 'WeixinJSBridge') { if (typeof WeixinJSBridge !== 'undefined') { WeixinJSBridge.invoke("getNetworkType", {}, () => { let ele = document.getElementsByTagName('iframe')[0]; ele.contentWindow.postMessage('WeixinJSBridge', '*'); }); } } }, false); }
- H5 内嵌短剧宝分享跳转商户页面流程图
sequenceDiagram
用户->>商户平台:0.用户登录商户平台
商户平台->>短剧宝API:1.用户A免密登录,传入redirectUrl
短剧宝API-->>短剧宝页面:2.跳转短剧宝页面,页面存入redirectUrl
用户->>短剧宝页面:3.用户A生成分享二维码(二维码路径为redirectUrl)
用户->>商户平台:4.用户B扫码
商户平台->>短剧宝API:5.用户B调用免密登录API
短剧宝API-->>商户平台:6.返回短剧宝分享页面url
商户平台-->>短剧宝页面:7.控制是否访问短剧宝内容页
2.2、重定向跳转模式
获取url
,直接访问地址 fullUrl
即可
2.3、APP嵌入实践
如果您是APP
场景进行短剧宝嵌入,可能会遇到如下几个问题:
- 短剧宝内微信、支付宝支付跳转对应
APP
无法跳转 - 部分视频播放在
iOS
端无法进行下一集切换问题 - 分享图片长按无法保存
针对上面两个问题,以Flutter
为例提供最佳实践方案(使用框架: flutter_inappwebview
)
2.3.1 短剧宝内微信、支付宝支付跳转对应APP
无法跳转问题
问题描述: 原生APP
内的嵌入短剧宝时,发现进入支付收银台页面点击微信/支付宝支付无法唤起对应微信/支付宝APP
.
解决方案: 监听跳转url
,判断监听url
是否包含跳转信息,包含指定微信/支付宝跳转信息则使用url_launcher(flutter)
框架进行实现跳转
如,当用户在短剧宝支付页面选择微信/支付宝支付时,接口会获取到跳转链接如下(例如):
微信地址示例:
支付宝地址示例:
截取微信url=
后内容:
此时监听到需要跳转的链接是 img.yeepay.com,且参数包含url=weixin
(支付宝方案一样,如:)字段,则使用flutter
语法url_launcher(flutter)
进行跳转(其他APP
开发语言类似)
代码示例:
//1. 截取跳转微信或者支付宝链接(根据自己链接需求截取)
//2. 转码跳转链接能读取使用
String decodedString = Uri.decodeComponent(urlStr);
await launchUrl(Uri.parse(decodedString),
mode:LaunchMode.externalApplication);
对应获取url
后跳转链接代码示例:
onLoadStart: (controller, url) async{
String urlPath = url.toString();
urlPath = Uri.decodeComponent(urlPath);
// 监听是否包含微信跳转
if ((urlPath.startsWith("https") || urlPath.startsWith("http")) && urlPath.contains("html?url=weixin")) {
controller.goBack();
await _launchUrl(urlPath);
// 监听是否包含支付宝跳转
}else if((!urlPath.startsWith("https") || !urlPath.startsWith("http")) && urlPath.startsWith("alipays://")){
await launchUrl(Uri.parse(urlPath), mode: LaunchMode.externalApplication);
}
},
onLoadStop:(controller, url){
String urlPath = url.toString();
if((!urlPath.startsWith("https") || !urlPath.startsWith("http")) && urlPath.startsWith("alipays://")){
controller.goBack();
}
},
.......
//跳转方法
Future<void> _launchUrl(String _url) async {
//截取跳转weixin链接(根据自己链接需求截取)
String url = Uri.decodeComponent(_url);
List<String> parts = url.split(".html?url=");
String urlStr = parts[1];
//转码跳转链接能读取使用
await launchUrl(Uri.parse(urlStr),
mode: LaunchMode.externalApplication);
}
2.3.2 iOS
端视频播放无法进行滑动切换剧集问题
**问题描述: ** APP
内的WebView
播放视频是使用iOS
原生视频播放框架(导致切换视频功能不完善).
问题原因: 我们的视频使用m3u8
或者mp4
格式(文件),使用WebView
框架默认是无权访问本地文件,需要设置WebView
可以访问本地文件并且关闭原生播放器播放播放框架.
解决方案: flutter
使用框架建议使用flutter_inappwebview
框架。flutter_inappwebview
框架自带处理iOS端读取本地文件关闭原生播放框架功能
代码如下:
InAppWebViewOptions viewOptions = InAppWebViewOptions(
mediaPlaybackRequiresUserGesture: false,
javaScriptCanOpenWindowsAutomatically : true,
preferredContentMode:UserPreferredContentMode.MOBILE,
allowUniversalAccessFromFileURLs: true,
allowFileAccessFromFileURLs : true
);
InAppWebViewGroupOptions(
crossPlatform: viewOptions,
ios:IOSInAppWebViewOptions(
allowsAirPlayForMediaPlayback: false,
allowsInlineMediaPlayback: true,
),
)
2.3.3 App 内分享图片长按无法保存
flutter 代码示例
onWebViewCreated: (controller) {
webViewController = controller;
controller.addJavaScriptHandler(
handlerName: 'nativeSaveImage',
callback: (args) {
_saveImageToNative(args[0]);
},
);
}
Android原生代码示例:
app内须实现nativeSaveImage接口。且在 saveImage 方法内自定义保存图片
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val webView: WebView = findViewById(R.id.webview)
CookieManager.getInstance().setAcceptCookie(false);
webView.clearCache(true);
webView.clearHistory();
val webSettings: WebSettings = webView.settings
webSettings.javaScriptEnabled = true
// 设置WebViewClient以在WebView内部加载页面
webView.setWebViewClient(WebViewClient());
webView.setWebChromeClient(WebChromeClient())
// 添加 JavaScript 接口 webView.addJavascriptInterface(WebAppInterface(this),"nativeSaveImage");
webView.loadUrl("https://www.example.com");
}
class WebAppInterface(private val mContext: Context) {
@JavascriptInterface
fun saveImage(baseImageUrl: String) {
// TODO: 解析baseImageUrll保存到本地
}
}
_saveImageToNative方法内传入一个String 要判断是否是Base64或HTTPS的根据不同的值进行保存.
2.3.4 App 内嵌短剧宝分享最佳实践
flutter 使用插件
uni_links2: ^0.6.0+2 # (Deep Links实现监听打开APP)
初始化
Initial Link // Uri parsing may fail, so we use a try/catch FormatException. try { final initialUri = await getInitialUri(); // Use the uri and warn the user, if it is not correct, // but keep in mind it could be `null`. } on FormatException { // Handle exception by warning the user their action did not succeed // return? }
监听跳转
```
import 'dart:async';
import 'dart:io';
import 'package:uni_links/uni_links.dart';
StreamSubscription _sub;
Future
// ... check initialLink
// Attach a listener to the stream
_sub = linkStream.listen((String? link) {
// 监听到h5传入信息,使用urlToMap(link)方法解析link里的参数使用参数调用免密登录接口获取跳转链接实现跳转到指定页面
}, onError: (err) {
});
}
- 读取参数
Map urlToMap(String urlStr){
Uri uri = Uri.parse(urlStr);
Map<String, String> params = uri.queryParameters;
return params;
}
- 制作 APP 的 H5 下载和打开分享页面
> 代码示例:该页面路径需要在请求免密接口时作为 redirectUrl 参数值
sequenceDiagram
用户->>商户平台:0.用户登录商户平台
商户平台->>短剧宝API:1.用户A免密登录,传入redirectUrl
短剧宝API-->>短剧宝页面:2.跳转短剧宝页面,页面存入redirectUrl
用户->>短剧宝页面:3.用户A生成分享二维码(二维码路径为redirectUrl)
用户->>商户平台:4.用户B扫码进入app中间页,将shareDjbPath参数透传
商户平台->>短剧宝API:5.用户B调用免密登录API
短剧宝API-->>商户平台:6.返回短剧宝分享页面url
商户平台-->>短剧宝页面:7.控制是否访问短剧宝内容页
2.4、添加桌面快捷方式PWA
模式的开启与关闭
用户使用独立域名访问短剧宝页面时,为方便用户二次使用,默认会弹出引导用户添加桌面快捷方式的引导。此弹窗对于内嵌至客户端应用系统业务场景,可能让用户产生困惑。所以,易链星云提供了无弹窗的解决方案,商户仅需要在免登录接口返回的targetUrl
基础上,添加pwa=false
参数即可。
链接示例:
2.5、小程序嵌入模式
小程序嵌入短剧宝,进行微信支付,会因为小程序的机制进行拦截,解决方法如下(如果不需要用微信支付,可以不用下面的配置)
- 登陆 **小程序开发平台**,在开发管理--> 业务域名 模块配置短剧宝域名;https://yc-duanjubao.yeeverse.top
- 下载校验文件,联系开发人员上传至短剧宝服务器根域名下
- 配置完成后,使用web-view嵌入短剧宝,web-view 不需要加任何样式
- 返回地址需要拼接参数:¤tPath=当前页面的路径, 注意:你的页面路径需要 encodeURIComponent 编码一下
如免密登陆接口返回地址为:
如你的加载短剧宝的路径为:
/pages/shortVideo/shortVideo
则url
跳转传:
示例代码
<!--index.wxml -->
<web-view bindmessage="bindmessage" src="{{ webViewUrl }}"/></view>
// index.js
Page({
data: {
webViewUrl: 'http://10.171.3.94:9095/djb-h5-fe/index.html#/pages/index/index?source=api&pwa=false&token=6d03d16dff1b444f8d547b3da5f5e3d1&platform=miniProgram¤tPath=/pages/djb/djb'
},
onLoad(e) {
if (e.keepPaypageAlive) {
this.setData({
webViewUrl: this.data.webViewUrl + '&keepPaypageAlive=' + e.keepPaypageAlive
})
}
},
// 监听到微信支付时,拉起易宝支付小程序(如无需微信支付,此方法可忽略)
bindmessage(e) {
console.log('接收到来自短剧宝的消息', e.detail.data[0])
const appId = e.detail.data[0].appId
const path = e.detail.data[0].path
wx.navigateToMiniProgram({
appId: appId,
path: path,
fail(e) {
// 绕过小程序的限制
if (e.errMsg == 'navigateToMiniProgram:fail can only be invoked by user TAP gesture.') {
wx.showModal({
title: '温馨提示',
content: '即将跳转微信支付',
complete: (res) => {
if (res.confirm) {
wx.navigateToMiniProgram({
appId: appId,
path: path,
})
}
}
})
}
}
})
},
})
三、短剧宝生产API
配置
待测试联调完成后,联系易链星云对接销售人员进行入网配置,入网完成后,下发对应后台商户登录账号后,即可进行生产数据参数配置
注意: 短剧宝API
需要通过《三、生成RSA
密钥》生成对应公私钥后,将公钥提供给对应的运营或技术支持人员进行配置
3.1、生成RSA
密钥
商户通过此地址https://open.yeepay.com/docs/open/platform-doc/developTools-sm/keyTools-sm 下载密钥生成器,生成RSA
公私钥。
注:私钥需要商户自己妥善保管。
3.2、密钥对接配置
将公钥提供给对应的运营或技术支持人员进行配置账号,相关人员配置完成后,即可进行测试调用。