小程序-发票助手在线文档

第三方APP调用小程序授权准备

1、进入微信开放平台创建企业自己的移动应用:https://open.weixin.qq.com/

image

2、填写基本信息,平台信息需要填写android、ios具体信息

image

image 3、提交审核通过后,进入该应用进行小程序关联

image

4、进行微信认证 image

第三方APP集成拉取小程序SDk

具体打开方式参考微信官方网站**

https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=21526646437Y6nEC&token=&lang=zh_CN

发票云小程序参数说明:

参数名 类型 是否必须 备注
userName String 小程序原始ID 生产:gh_bfa83b5cd310 测试:gh_cb39a37583e3
path String 跳转小程序路径 "pages/index/index?scene=${userKey}&source=app" userKey由下方接口获取
miniprogramType int 小程序模式 0

Android开发示例

开发前需到微信开发平台下载Android开发工具包(SDK),可在“资源中心-开发资源-资源下载-Android资源下载”按照指引操作。

调用接口:WXLaunchMiniProgram

移动应用跳转到小程序示例:


String appId = "wxd930ea5d5a258f4f"; //微信开发平台创建的应用AppId
IWXAPI api = WXAPIFactory.createWXAPI(context, appId);

WXLaunchMiniProgram.Req req = new WXLaunchMiniProgram.Req();
req.userName = ${userName};
req.path = ${path};
req.miniprogramType = ${miniprogramType};
api.sendReq(req);

回调说明

//WXEntryActivity中

public void onResp(BaseResp resp) {
    if (resp.getType() == ConstantsAPI.COMMAND_LAUNCH_WX_MINIPROGRAM) {
        WXLaunchMiniProgram.Resp launchMiniProResp = (WXLaunchMiniProgram.Resp) resp;
        String extraData =launchMiniProResp.extMsg; //对应小程序组件 <button open-type="launchApp"> 中的 app-parameter 属性
    }
}

iOS开发示例

开发前需到微信开发平台下载iOS开发工具包(SDK),可在“资源中心-开发资源-资源下载-iOS资源下载”按照指引操作。

移动应用跳转到小程序示例:


WXLaunchMiniProgramReq *launchMiniProgramReq = [WXLaunchMiniProgramReq object];
launchMiniProgramReq.userName = ${userName};
launchMiniProgramReq.path = ${path};
launchMiniProgramReq.miniProgramType = ${miniProgramType};
return  [WXApi sendReq:launchMiniProgramReq];

回调说明


-(void)onResp:(BaseResp *)resp 
{
     if ([resp isKindOfClass:[WXLaunchMiniProgramResp class]])
     {
          NSString *string = resp.extMsg;
          // 对应JsApi navigateBackApplication中的extraData字段数据
     }
}

第三方APP内部是H5页面对接则需要向前端提供js桥方法进行对接,如文档下方“云之家案例”

对接约定

发票云API采用RESTfull的设计方式,客户端通过HTTP协议的GET、POST(目前主要是这两种)方法请求进行调用。
Restful:参考资料
http://www.infoq.com/cn/articles/designing-restful-http-apps-roth
API的调用地址基本上遵循如下约定的方式:
API地址前缀+ 服务 + 资源+ 参数

测试环境: baseUrl = https://api-dev.piaozone.com/test

正式环境: baseUrl = https://api.piaozone.com

对接过程中需要:发票云授权标识(client_id)、授权密钥(client_secret)、加密密钥(encrypt_key)进行授权,每个环境的授权不同,获取方式可以找实施人员协助提供

image

接口返回约定: 若无特别说明,接口返回如下

{
    "errcode": "0000", ///成功时为0000, 失败时为非0000
    "description": "操作成功", //描述,失败时可以根据这个字段进行提示
    "data": "" //请求的返回实际数据
}

接口签名约定: 支持以下3种签名生成方式:

1、MD5(encType=0) 代码示例:

MD5(client_id + client_secret + timestamp)

2、SHA256(encType=1) 代码示例:

DigestUtils.sha256Hex(client_id + client_secret + timestamp)

3、HMACSHA256(encType=2) 代码示例:

HMACSHA256.sha256_HMAC((client_id + client_secret + timestamp), client_secret)

HMACSHA256 工具类代码示例:

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class HMACSHA256 {
    /**
     * 将加密后的字节数组转换成字符串
     *
     * @param b 字节数组
     * @return 字符串
     */
    public  static String byteArrayToHexString(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b!=null && n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1)
                hs.append('0');
            hs.append(stmp);
        }
        return hs.toString().toLowerCase();
    }
    /**
     * sha256_HMAC加密
     * @param message 消息
     * @param secret  秘钥
     * @return 加密后字符串
     */
    public static String sha256_HMAC(String message, String secret) {
        String hash = "";
        try {
            Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
            sha256_HMAC.init(secret_key);
            byte[] bytes = sha256_HMAC.doFinal(message.getBytes());
            hash = byteArrayToHexString(bytes);
        } catch (Exception e) {
            System.out.println("Error HmacSHA256 ===========" + e.getMessage());
        }
        return hash;
    }
}

调用小程序前,需要先获取userKey

获取userKey接口地址: baseUrl/m4/bill/wmp/get/user/key?access_token=${access_token}

请求方式: POST

请求content-type格式: application/json

请求的全部参数:

token的获取方式和其它接口一致

参数名 是否必须 备注
bxd_key 报销单ID,如果需要同步附件信息,则必传
timestamp 用于签名验证
clientId 发票云分配的应用clientId
ghf_mc 购货方名称(校验发票抬头是否一致时使用)
tin 企业税号
cid 接入企业的用户ID
sign 签名, 签名规则见对接约定, clientSecret和clientId由发票云分配
encType 签名加密类型 0:MD5、1:SHA256、2:HMAC SHA256,不传默认MD5加密
random 随机码
ticketParam 过滤规则,格式如:1101
salerName 单据限制销方名称
salerTaxNo 单据限制销方税号
billType 单据类型
assembleByBillType 是否按单据类型(billType)组装推送给APP的数据。 1:组装; 其他不组装
optMode 非必须,操作模式 ,0正常(默认) 1仅附件上传

ticketParam 参数规则顺序: 0 不允许 1 允许

第1位:已报销过的发票是否允许导入

第2位:发票抬头与企业名称不一致的发票是否允许导入

第3位:发票抬头与税号不一致发票是否允许导入

第4位:发票查验不通过的发票是否允许导入

billType参数规则:

参数值 备注
Tra 智能差旅行程单
Pur 智能物品采购报销单
BizOut 智能对公报销单
"" 费用报销单传空字符串

请求的返回值:

    {
        "errcode": "0000", ///成功时为0000, 失败时为非0000
        "description": "操作成功", //描述,失败时可以根据这个字段进行提示
        "data": {
            "userKey": "" //获取到的userKey
        }
    }    

导入发票跳转回App或小程序后,再次导发票请重新获取userkey避免失效

获取授权access_token

API地址前缀:基本不变的API访问URL地址,如下:

流程图:

image

接口请求说明:

http请求方式:POST

获取token接口地址: baseUrl/base/oauth/token

请求数据结构

{
    "client_id":"vc0c6hjlgnKCic",
    "sign":"8b9c78fccedbb72eff1106c57505b8f6",
    "encType": 0,
    "timestamp":1463468225650
}
**返回数据结构:**
{
    "access_token":"7ffe2cced2a0e3615a24705ee39f53d09a243911c0e932991570ffa1d86abbf4",
    "token_type":"bearer",
    "expires_in":43200,
    "errcode":"0000",
    "description":"操作成功"
}

请求参数描述:

参数 类型 是否必须 描述
client_id string 发票云授权标识
sign String 签名,签名规则见对接约定
encType Int 签名加密类型 0:MD5、1:SHA256、2:HMAC SHA256,不传默认MD5加密
timestamp long 时间戳

返回参数描述:

参数 类型 描述
access_token string 字符串, 最长255,用于后续访问的令牌,随机字符串
token_type String 字符串,令牌类型,可以不用关注, 目前固定bearer
expires_in string 长整型,令牌在多少秒内有效,单位为秒
errcode String 操作结果码
description string 操作描述

失败信息:

{
    "error":"invalid_client",
    "error_description":"Bad client credentials"
}

案例 云之家轻应用

接口调用代码(js桥需客户方自己提供....)

xuntongJSBridge.call(
    'launchMiniprogram',{
        'userName':'xxx',
        'path':'yyyyy',
        'miniprogramType': 'zzz',
        ....
    },
    function(result){

    }
);

参数说明

参数 类型 描述
userName String 小程序原始ID ("gh_bfa83b5cd310") 注意:每个环境对应的小程序不同,则原始ID要注意更换
path String "pages/index/index?scene=${userkey}&source=app";拉起小程序页面路径(app为固定值)
miniprogramType Int 0 小程序的版本

回调说明

{
    success : true or  false  是否成功(String),
    error:错误信息(String),
    errorCode:错误码(int//
    data:{
         extraData:’xxx'    //小程序返回的发票、附件信息
    }
}

常见的对接问题

提示二维码失效

注:出现上述图1,图2问题,解决流程如下。

1:先通过图3(我的--新手指导)检查当前环境是什么小程序。

2:检查客户配置(如下图: 苍穹费用报销)的发票云小程序id和小程序模式是否跟图3匹配。

image

参数说明

名称 userName miniprogramType 对应环境 小程序版本名称
金蝶发票云 gh_bfa83b5cd310 0 生产 api.piaozone.com pwy-release x.x.x
票无忧100 gh_cb39a37583e3 0 测试 api-dev.piaozome.com/test pwy-test x.x.x

注:

1: 云之家客户仅绑定了一个小程序,因此,切换环境调试时,只需要修改对应的 miniprogramType 值,userName 不变; 对接测试环境时跳转小程序后可能需要给用户添加访问权限

2: 其他App客户如果同时绑定 金蝶发票云 和 票无忧100 了,则只需要调整userName即可切换调试环境

小程序返回App并传递发票,附件数据

数据接收方式请参考上面android和ios示例中的回调部分

发票云返回的数据格式如下

消息格式: {"type":"selectedInvoices", data: {}}

type为saveBill时为导入时的发票数据

费用单据的数据格式:billType为空字符串

{
    type: "selectedInvoices", //选择的发票信息
    data: { //发票信息
        invoicesAmount: 1000, //价税合计总额
        invoicesTaxAmount: 200, //总税额
        serialNos: "044031600113,044031600113", //发票流水号,逗号分隔
    },    
    attachData: [{ //附件信息
        "attachmentName": "附件984775",  //附件名称
        "gatherTime": "2019-01-30", //附件采集时间
        "serialNo": "b115345688f64c519b047943bb2d11b38", //附件流水号
        "snapshotUrl": "http://api.kingdee.com/kdrive/user/file/thumbnail?client_id=200242&file_id=54947544&quality=100&scode=WXZRZnl2dl9MMWZJVi9MSUhlXzcx&sign=1c2e361c97aa438f6ae9c409107d2e2f9472c44f&width=800", //附件快照地址
        "remark": "- -" //附件备注
    }]
}

//如果需要或发票的完整信息, 请通过接口(如下)传入发票流水号获取发票完整数据

智能差旅行程单的数据数据格式, billType为:Tra

{
    "type": "selectedInvoices",
    "data": {
        "billnumber": "2018-11-28-00001898", //单据编号
        "bxd_key": "Ik2TrOC5RSCOND0pE9J8=", //单据全球唯一key
        "data": [{
            "invoiceData": [{
                "salerAccount": "中国银行5218666111888",  //销方银行名称和账号
                "stationGeton": "",  //
                "classify": "other", //分类
                "amount": 50.91,  //不含税金额,飞机票则采用票价
                "salerName": "航信培训企业", //销方名称
                "invoiceDate": "2018-09-29", //开票日期
                "invoiceCode": "", //发票代码
                "serialNo": "", //发票流水号
                "totalAmount": 56, //价税合计
                "taxRate": "0.099980", //税率
                "seatGrade": "",
                "salerAddressPhone": "广东省深圳市南山区 xxxx", //销方地址电话
                "stationGetoff": "",
                "salerTaxNo": "", //销方税号
                "invoiceType": "1", //发票种类,1.普通电子发票2.电子发票专票3.普通纸质发票4.专用纸质发票5.普通纸质卷票7.通用机打8.的士票9.火车票10.飞机票11.其他 12.机动车 13.二手车14.定额发票15.通行费16.客运票17.过路过桥发票19.完税证明20.轮船票23通用机打电子发票
                "goodsCode": "3079900000000000000", //商品编码
                "taxAmount": 5.09, //总税额
                "invoiceNo": "", //发票号码
                "airItemsArr": [],  //如果是机票,可能有多条行程
                "goodsName": "*水果*苹果",  //商品名称
                "classifyName": "其他费用" //分类中文名
            }],
            "totalAmount": 60.3,
            "amount": 54.97,
            "costName": "其他费用",
            "size": 2,
            "costType": "other",
            "taxAmount": 6.33,
            "invoiceDate": "2018-09-29"
        }],
        "invoicesTotalAmount": 76.98,//发票金额总和
        "invoicesTaxAmount": 5.82,//发票税额总和
        "allSize": 3,//导入该单据的发票总数量
        "invoiceSerialNos":"e7601aa2a677467bbf0271fbde682d910,e7fde3adddbe46328aa3405085854c250",//当前单据导入的发票流水号集合
        "isElectronicInvoice": false,//当前单据导入的发票是否全是电票,true为是,false为否
        "userid": "eid-1544420896749"//对应用户的eid
    }
}

智能物品采购单据-数据数据格式, billType为:Pur

{
    "type": "selectedInvoices",
    "data": {
        "billnumber":"", //对应传入的billnumber
        "bxd_key":"", //对应传入的bxd_key
        "type":"", //前端约定交互方式识别
        "userid":"", //对应传入的eid
        "isElectronicInvoice": false,//当前单据导入的发票是否全是电票,true为是,false为否
        "invoiceSerialNos":"e7601aa2a677467bbf0271fbde682d910,e7fde3adddbe46328aa3405085854c250",//当前单据导入的发票流水号集合
        "data":
            {
                //收款方信息集合
                "buyerInfo":[
                    {
                        "buyerName":"", //收款方企业名称
                        "buyerTaxNo":"", //收款方企业税号
                        "buyerAddressPhone":"", //收款方地址电话
                        "buyerAccount":"", //收款方银行帐号
                    }
                ],

                //采购列表信息,按供应商、品名划分
                "purchaseInfo":[
                    {
                        "salerName":"", //销方名称,即供应商名称
                        "salerTaxNo":"", //销方税号,即供应商税号
                        "goodsName":"", //品名
                        "goodsCode":"", //品名对应的商品编码
                        "invoiceDate":"", //日期,默认取第一个
                        "unit":"", //单位,默认取第一个
                        "num":"", //数量,数量总和
                        "unitPrice":"", //单价,平均单价
                        "detailAmount":"", //金额
                        "taxAmount":"", //合计税额
                        "size":"", //品名条数
                        "invoiceData":[] //发票流水号集合
                    }
                ]
            }
        }
    }

智能对公单据-数据数据格式, billType为:BizOut

{
    "type": "selectedInvoices",
    "data": {
        "billnumber": "RH-365-20181117-1054", //单据编号
        "bxd_key": "iCFJ7WdQTrxjJTCkx2fAmKk7FTnPXj=", //单据ID
        "type": "savebill",
        "userid": "", //用户eid
        "invoicesTotalAmount": 1000, //总价税合计总额
        "invoicesTaxAmount": 200, //总税额
        "data": [  //按税号、银行帐号划分
        {
            "salerTaxNo":"", //开票方税号
            "salerName":"", //开票方名称
            "salerAddressPhone":"", //开票方地址、帐号
            "salerAccount":"", //开票方银行开户行、帐号
            "amount": 1000,  //总金额(不含税)
            "taxAmount": 200, //总税额
            "totalAmount":"", //价税合计总额
            "invoiceType":"", //发票类型
            "invoiceNo":"", //发票号码
            "invoiceCode":"",//发票代码
            "goodsCode":"", //税收分类编码,发票数据中取第一条信息为准
            "goodsName":"", //货物名称,发票数据中取第一条信息为准
            "taxRate":"", //税率    ,发票数据中取第一条信息为准
            "serialNo":"" //发票流水号
        }]
    }
}

以上操作为移动端界面通过小程序采集发票流程

报销单据与发票关系处理(重点)

请查看接口文档11.0: http://imgdev-master.piaozone.com/docs/easNew-fpzs.html

报销单据上查看关联发票(重点)

1.1 通过报销单ID查询

接口地址:baseUrl/m4/fpzs/bxdInvoices?bxd_key={报销单ID}&client_id={创建当前报销单的发票云授权标识}&timestamp={时间戳}&sign={签名}

请求方式: GET

参数说明

参数 类型 描述
bxd_key String 报销单ID
client_id String 创建当前报销单的发票云授权标识
timestamp String 时间戳
sign String 签名规则见对接约定, clientSecret和clientId由发票云分配
encType Int 签名加密类型 0:MD5、1:SHA256、2:HMAC SHA256,不传默认MD5加密

请求接口参数示例

{
    "client_id": "创建当前报销单的发票云授权标识",
    "sign": "签名:见对接约定",
    "encType": 0,
    "timestamp": "时间戳",
    "bxd_key": "weqasdaqweqweqe"
}

1.2 单据分录上通过事件代码查询

接口地址:baseUrl/m4/fpzs/expense/invoices/certifacate/query?event_code=${eventCode}

请求方式: GET

参数说明

参数 类型 描述
envent_code String 事件代码(如下接口获取)

1.2.1获取事件代码eventCode=f2adb2cb45a3ac1b129c581102a0d266

接口地址: baseUrl/base/oauth/eventCode

请求方式: POST

请求content-type格式: application/json

参数说明

参数 类型 描述
client_id String 创建当前报销单的发票云授权标识
timestamp String 时间戳
sign String 签名规则见对接约定, clientSecret和clientId由发票云分配
encType Int 签名加密类型 0:MD5、1:SHA256、2:HMAC SHA256,不传默认MD5加密
type String EXPENSE_SERIALNO_QUERY //固定值
buyerName String 购方抬头
buyerTaxNo String 购方税号
ticketParam String 发票校验过滤规则,格式如:1101
certifacateSerialNos Array 附件流水号
invoiceSerialNos Array 发票流水号
expenseId String 报销单ID

请求接口参数示例

{
    "client_id": "创建当前报销单的发票云授权标识",
    "data": {
        "buyerName": "校验发票抬头企业名称不一致提示",
        "buyerTaxNo": "校验发票抬头税号不一致提示",
        "certifacateSerialNos": [
            "附件流水号1",
            "附件流水号1"
        ],
        "invoiceSerialNos": [
            "发票流水号1",
            "发票流水号2"
        ],
        "ticketParam": "发票校验过滤规则,格式如:1101",
        "expenseId":"报销单ID"
    },
    "sign": "签名:见对接约定",
    "encType": 0,
    "timestamp": "时间戳",
    "type": "EXPENSE_SERIALNO_QUERY"
}

小程序跳转小程序对接收票流程

客户小程序跳转

wx.navigateToMiniProgram({
  appId: '${appId}',
  path: 'pages/index/index?scene=${userKey}'+'&source=mini',
  envVersion: 'release',
  success(res) {
    // 打开成功
  }
})

参数说明

参数 类型 描述
appId String 目标小程序应用ID(测试环境:wxb5de26c7f5dc89c6 ,生产环境:wxa3a3844ae7205f76 )
userKey String 由上诉接口获取

客户小程序接收

//app.js
onShow: function (options) {
    if (options.referrerInfo){
          const { extraData } = options.referrerInfo
          const {  miniData } = extraData; //数据说明如下
     }
}

数据说明

{
    "type":"selectedInvoices", //固定值
    "data":{
        "invoicesAmount":3540, //发票总金额
        "invoicesTaxAmount":102.39, //发票总税额
        "serialNos":"0e9db98b85aa4aec9d6d340be1e199db0,aa43d7084afe4ca690c371d7e5b5498e0," //发票流水号集合
    },
    "attachData":[ //附件流水号集合
        "c395e1d0236c483b8fc3a4ae0f3dd52b1",
        "590a6a40e3b740d597c0b044a819a6580"
    ]
}

通过流水号获取发票,附件详情

查看文档http://imgdev-master.piaozone.com/docs/easNew-fpzs.html#wow46