首先先附上微信電子發票報銷方接口列表的官方鏈接,如果你是.NET
開發,那麼你可以通過Senparc.Weixin.MP來快速開發相應的微信電子發票功能,該功能在其16.4.0
版本開始提供,16.4.9
版本修復報銷方的bug(修復PR代碼由本人提供),16.4.10
電子發票原作者又做了一些調整,重新定義了發票實體,所以電子發票部分16.4.9
和16.4.10
不兼容,然後目前16.4.10
報銷方部分已在實際生產環境進行過測試,目前尚未發現報銷方相關的功能存在bug,商戶和開票平臺部分因爲沒實際業務需求,所以無法進行測試。
略去前端部分的內容,對於電子發票報銷方,微信提供了以下兩類API:
- 查詢發票
- 更新狀態
然後每類API都默認提供單個以及批量兩種操作,所以總共是4個API接口。
在Senparc.Weixin.MP
中,電子發票相關代碼均在Senparc.Weixin.MP.AdvancedAPIs.InvoiceApi
靜態類下,每個靜態方法對應一個API接口,所有的方法均採用了同種代碼方式,接下來就要說到開發中遇到的坑了,當然有些是SDK的坑,有些是微信API自身的坑。
先簡單的列下接入示例代碼
string appId="";
string appSecret="";
//接下來只需要簡單的通過調用InvoiceApi.XXX方法就可以來完成調用某個API
//以獲取指定單張電子發票爲例
string card_id;//發票卡券的 card_id
string encrypt_code;//發票卡券的加密 code ,和 card_id 共同構成一張發票卡券的唯一標識
var inv = InvoiceApi.GetInvoiceInfo(appId,card_id,encrypt_code);
//當然每個API方法都有對應的異步方法,比如GetInvoiceInfoAsync
總的來說,接入還是相當簡單的,所以接下來就要講第一個坑了
1、微信報銷方批量獲取發票列表的坑
你可以通過InvoiceApi.GetInvoiceListInfo
來批量獲取發票列表,此時微信會返回你所請求的所有card_id
和encrypt_code
所對應的發票信息,因爲發票的報銷涉及狀態變更,所以card_id
和encrypt_code
我們是需要在本地持久化存儲的,那麼我們也就需要將請求的card_id
和encrypt_code
與返回的發票進行一一對應,然後關鍵點來了,返回的集合信息中,每張發票只有card_id
,沒有encrypt_code
,如果你沒注意到微信文檔上描述的發票卡券的加密 code ,和 card_id 共同構成一張發票卡券的唯一標識
,直接按card_id
去與請求的card_id
做對應的話,那麼就有可能會導致錯誤,所以接下來就是關鍵點:微信返回的列表與請求參數是一一對應的,所以可以簡單的按索引來匹配,但我沒測試假如請求的數據中有一條是錯誤的,返回的結果會怎麼樣,有興趣的可以試驗下
List<InvoiceItem> itemList;
var invList = InvoiceApi.GetInvoiceListInfo(token, itemList);
if (invList.errcode == 0)
{
for(var i=0;i<invList.item_list.Count;i++)
{
//兩者一一對應
var item = itemList[i];
var inv = invList.item_list[i];
//當然如果你不放心這種寫法,那麼可以採用下一種
var currentItem = itemList.First(m => m.card_id == inv.card_id);
itemList.Remove(currentItem);//通過移除已經匹配到請求參數的來保證不會重複匹配
}
}
2、發票狀態更新的坑
因爲按正常的流程,當你從微信卡包成功獲取發票信息、並持久化在你本地進入報銷流程時,你需要通過微信的API來更新發票狀態爲INVOICE_REIMBURSE_LOCK
發票已鎖定;相應的假如你在本地刪除了該發票,你也需要告知微信目前該發票狀態要更改爲INVOICE_REIMBURSE_INIT
發票初始狀態,未鎖定;如果報銷完成了,則你需要更新狀態INVOICE_REIMBURSE_CLOSURE
發票已覈銷。而此處的坑就在於如果發票在微信那裏狀態本身爲INVOICE_REIMBURSE_INIT
,你再通過InvoiceApi.UpdateInvoiceStatus
去更新發票爲INVOICE_REIMBURSE_INIT
狀態時,即更新爲同種狀態,微信會返回更新失敗……當然這也不能說微信錯了,只能說這塊響應不友好,但我們業務中爲了保證發票能夠解鎖,如果解鎖失敗就不允許本地刪除發票(因爲在微信卡包中,鎖定狀態的發票是不能刪除的,也無法再次被選擇,所以假如你本地刪除了card_id和encrypt_code,那麼那幾張灰色的發票信息就要在你的卡包裏一輩子了,因爲你再也沒有正常途徑可以取到card_id和encrypt_code!!!),所以此處如果你更新發票狀態失敗了,你可以再通過InvoiceApi.GetInvoiceInfo
再獲取一次發票信息,判斷下發票目前狀態實際爲何種狀態,如果發票狀態已經爲你想要更改的狀態,那麼就可以繼續執行後續的業務邏輯。
3、微信公衆號Token
因爲微信公衆號AccessToken有時間限制,所以爲了貪圖方便,我們可能會直接每次都通過AccessTokenContainer.TryGetAccessToken(appId,appSecret,true)
強制刷新AccessToken,測試期間可能會發現一切正常,但AccessToken其實每天都有刷新次數上限(2000),所以如果上了生產環境還每次都強制刷新AccessToken的話,可能很快就會達到每日上限!!!另外如果你們有多個環境(假設A/B兩個環境),而每個環境都用了同樣的appId,那麼恭喜你,只要你在A訪問了一次微信相關的功能獲取了一個AccessToken,然後在B又操作了一次微信相關的功能又獲取了一個新的AccessToken,那麼5分鐘之後A的AccessToken就過期,而且因爲expire_in還沒到,框架也不會幫你自動刷新Token,然後A環境所有與微信相關的功能都game over……
4、Ip白名單
微信公衆號在設置時需要配置Ip白名單,只有白名單內指定的Ip微信纔會允許在Jsapi中訪問相關功能,所以如果你的服務器是通過代理服務器上網,那麼就很可能存在對外Ip地址發生變化的問題,而且最坑的是:Ip地址不在白名單時,提示是簽名錯誤!