Pages

2015-09-30

國泰世華ATM虛擬帳號編碼

網購業者收款確認的事一向是最煩鎖的事,若有公司營業登記,可以向國泰世華申請ATM虛擬帳號,就可以發給買家匯款指定的帳號,等入帳後再使用銀行提供的即時對帳API(存取頻率最低3分鐘)作查稽。

國泰世華發行的虛擬帳號長度有11, 14及16字長,前2者可以使用臨櫃匯款的方式繳納(匯款單的帳號長度是14長)。若有限繳時間的檢查及訂單編號的綁定,則使用16字長編碼才夠。

編號原則: 16位編碼 = 企業代碼[4] + 限繳日期MMDDHH[6] + 自訂編碼[5] + 檢核查[1]

送交銀行的申請表單勾核選項如下:

ATM Apply

使用國泰ATM虛擬帳號不需要事先在銀行開立允許列表,只要符合其編碼原則文件規範即可。

ATM Encoding

發現這虛擬帳號的檢核碼規則,跟台灣身份字號的最後1碼是邏輯相同,只差別在於權數不同而己。我的程式應用是採用16位帳號+限繳日期yyymmhh+交易金額+檢核碼的驗證方式,最後2組檢核碼相加須要取餘數,這點官方文件沒提到。

分享我的國泰世華16位虛擬帳號的C#源碼下:

void Main()
{
MyVirtualAccount provider = new MyVirtualAccount();

// 限繳日期設定
DateTime expDate = new DateTime(2015, 08, 31, 12, 00, 00);

// 產生10筆16位長編碼
for (int i = 1; i <= 10; i++)
{
var s = provider.CreateAccount16(
prefix4: "9999"
, expDate: expDate
, billNo5: i.ToString().PadLeft(5, '0')
, amount: 4);

s.Dump();
}
}

///
/// 國泰世華-虛擬帳號產碼類別
///

/// Author: Tomex Ou
/// Update: 2015-08-25 13:56:30
public class MyVirtualAccount
{

///
/// Creates Len=16 Account
///

/// Customer ID (Len=4), 公司編碼="9999"
/// Expired Date (GetNextFullHour)取下個整時刻
/// Bill No (Len=5, 左補0)
/// Amount value金額
///
public string CreateAccount16(string prefix4, DateTime expDate, string billNo5, decimal amount)
{
// -------------------------------------------------------------
// Arguments Validating.
// -------------------------------------------------------------
if (prefix4.Length != 4)
throw new Exception("The length of Customer ID must be 4.");

if (billNo5.Length != 5)
throw new Exception("The length of Bill No. must be 5.");


int accCheck, amtCheck, value;

// -------------------------------------------------------------
// accCheck for Account
// -------------------------------------------------------------
string strAccount = prefix4 + this.GetNextFullHour(expDate).ToString("MMddHH") + billNo5;
int[] accWeights = { 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // Len=15
int accSum = 0;
for (int i = 0; i < accWeights.Length; i++)
{
value = (int)Char.GetNumericValue(strAccount[i]); // char '0' to int32.
accSum += (value * accWeights[i]) % 10;
}

accCheck = this.GetCheckValue(accSum);


// -------------------------------------------------------------
// amtCheck for Amount
// -------------------------------------------------------------
int[] amtWeights = { 8, 7, 6, 5, 4, 3, 2, 1 };
int[] amtIntArray = this.CreateAmountIntArray(amount, amtWeights.Length);
int amtSum = 0;
for (int i = 0; i < amtWeights.Length; i++)
{
value = amtIntArray[i];
amtSum += (value * amtWeights[i]) % 10;
}

amtCheck = this.GetCheckValue(amtSum);


// -------------------------------------------------------------
// Full check
// -------------------------------------------------------------
int fullCheck = (accCheck + amtCheck) % 10;

return strAccount + fullCheck;
}


///
/// 取得Sum值的檢核碼
///

private int GetCheckValue(int sum)
{
int d0 = sum % 10; // 個位數
return (d0 == 0) ? 0 : 10 - d0;
}



///
/// 限繳日期的下一個整點時刻
///

public DateTime GetNextFullHour(DateTime theDate)
{
double nextFullHour = Math.Ceiling(theDate.TimeOfDay.TotalHours);
return theDate.Date.AddHours(nextFullHour);
}


///
/// 金額的int陣列 (8位長)
///

private int[] CreateAmountIntArray(decimal amount, int totalWidth = 8)
{
// 1200.00 => 1200 => ["00001200"]
string strAmount = decimal.ToInt32(amount).ToString().PadLeft(totalWidth, '0');

if (strAmount.Length != 8)
throw new Exception("The length of amount must <= 8.");

int[] intArray = new int[strAmount.Length];
for (int i = 0; i < strAmount.Length; i++)
{
intArray[i] = (int)Char.GetNumericValue(strAmount[i]); // char '0' to int32
}

return intArray;
}

}

發行的虛擬帳號在轉帳時,必須符合限繳時間+交易金額+檢核碼的規則,否則轉帳時會出現錯誤,無法入帳。


ATM Error

2015-09-23

XML+LINQ片段讀取解析

讀取XML資料雖然方法各異,但第一原則還是得考慮效能問題,若是來源XML資料是很龐大,就必須使用XmlReader循序讀入,而非貪圖方便用XML DOM類別(XmlDocument, XDocument)讀入記憶體,例如「台灣3+2郵遞區號XML查詢」的檔案讀取。若是片段的XML字串,則以語法搜尋簡便為原則,以前使用XmlDocument + XPath方式讀取資料,但新式做法用XDocument + LINQ更為簡便。

以下是C#範例程式碼:

void Main()
{
string xmlString = @"



10058
臺北市
中正區
八德路1段



10079
臺北市
中正區
三元街
單全



".Trim(); // XML前面不能有空白

//Elements
try
{
// XML字串若有錯誤,在Parse()會丟出例外
var xDoc = XDocument.Parse(xmlString);

// xDoc.Root =
var items = from x in xDoc.Root.Elements("Zip32")
select new Zip32Item {
// Element(name)可能為null
Zip5 = x.Element("Zip5").Value.Trim()
, City = x.Element("City").Value.Trim()
, Area = x.Element("Area").Value.Trim()
, Road = x.Element("Road").Value.Trim()
, Scope = x.Element("Scope").Value.Trim()
};

// 列出內容
items.Dump();
}
catch (Exception ex)
{
ex.ToString().Dump();
}

}

// XML節點強型別Class宣告
public class Zip32Item
{
public string Zip5 { get; set; }
public string City { get; set; }
public string Area { get; set; }
public string Road { get; set; }
public string Scope { get; set; }
}

讀出XML結果為:


image


以上是將XML片段讀取並變成強型別Class的簡易方式。若能選擇來源格式,使用JSON字串會是更便潔好用的方式,因為它能直接反序列化為物件。

2015-09-16

微信WeChat公眾平台

微信公眾平台就是騰訊官方開放微信的訊息收發API,讓組織或企業在微信APP裏建立自己的組識平台,可以讓用戶透過微信關注及訂閱該群組,就可以查詢及推送訊息,與用戶互動。這些預先建立的圖文,可以在微信公眾平台裏的網站定義好,定好一些訊息規則,就能主動推送給用戶。例如下圖是我的一個微信作品:

WeChat App

圖文訊息除了事先定義外,微信平台也允許在收到特定訊息時就主動POST資料到指定的網址,因此可以存取自身的資料庫,再將查詢結果回信至微信APP給用戶。整個訊息流運作如下圖:(此圖來源:這兒)

WeChat Flow

微信官方企業號API文件: http://qydev.weixin.qq.com/wiki/index.php

Senparc.Weixin.MP是第三方提供的微信C#版SDK,可以簡化開發工作,而且是開源免費的,中文學習教程在這兒

2015-09-09

字串編碼Encoding CodePage/CodeName List

很多公家機關及銀行資料查詢仍是以big5為主,假如沒有歷史包袱,新的程序就儘量使用utf-8編碼吧!

// Converts big5 into utf-8 encoding.
byte[] big5Bytes = XXX();
byte[] utf8Bytes = Encoding.Convert(Encoding.GetEncoding("big5"), Encoding.UTF8, big5Bytes);
string result = Encoding.UTF8.GetString(utf8Bytes);


要產生指定的CodePage(int)及CodeName(string)的方式:

using System.Text;

void Main()
{
// For every encoding, get the property values.
foreach (EncodingInfo ei in Encoding.GetEncodings() )
{
Encoding e = ei.GetEncoding();
Console.WriteLine("{0,-6} {1}", ei.CodePage, ei.Name);
}
}

產生的列表如下:


Encoding CodePage List

37     IBM037
437    IBM437
500    IBM500
708    ASMO-708
720    DOS-720
737    ibm737
775    ibm775
850    ibm850
852    ibm852
855    IBM855
857    ibm857
858    IBM00858
860    IBM860
861    ibm861
862    DOS-862
863    IBM863
864    IBM864
865    IBM865
866    cp866
869    ibm869
870    IBM870
874    windows-874
875    cp875
932    shift_jis
936    gb2312
949    ks_c_5601-1987
950    big5
1026   IBM1026
1047   IBM01047
1140   IBM01140
1141   IBM01141
1142   IBM01142
1143   IBM01143
1144   IBM01144
1145   IBM01145
1146   IBM01146
1147   IBM01147
1148   IBM01148
1149   IBM01149
1200   utf-16
1201   utf-16BE
1250   windows-1250
1251   windows-1251
1252   Windows-1252
1253   windows-1253
1254   windows-1254
1255   windows-1255
1256   windows-1256
1257   windows-1257
1258   windows-1258
1361   Johab
10000  macintosh
10001  x-mac-japanese
10002  x-mac-chinesetrad
10003  x-mac-korean
10004  x-mac-arabic
10005  x-mac-hebrew
10006  x-mac-greek
10007  x-mac-cyrillic
10008  x-mac-chinesesimp
10010  x-mac-romanian
10017  x-mac-ukrainian
10021  x-mac-thai
10029  x-mac-ce
10079  x-mac-icelandic
10081  x-mac-turkish
10082  x-mac-croatian
12000  utf-32
12001  utf-32BE
20000  x-Chinese-CNS
20001  x-cp20001
20002  x-Chinese-Eten
20003  x-cp20003
20004  x-cp20004
20005  x-cp20005
20105  x-IA5
20106  x-IA5-German
20107  x-IA5-Swedish
20108  x-IA5-Norwegian
20127  us-ascii
20261  x-cp20261
20269  x-cp20269
20273  IBM273
20277  IBM277
20278  IBM278
20280  IBM280
20284  IBM284
20285  IBM285
20290  IBM290
20297  IBM297
20420  IBM420
20423  IBM423
20424  IBM424
20833  x-EBCDIC-KoreanExtended
20838  IBM-Thai
20866  koi8-r
20871  IBM871
20880  IBM880
20905  IBM905
20924  IBM00924
20932  EUC-JP
20936  x-cp20936
20949  x-cp20949
21025  cp1025
21866  koi8-u
28591  iso-8859-1
28592  iso-8859-2
28593  iso-8859-3
28594  iso-8859-4
28595  iso-8859-5
28596  iso-8859-6
28597  iso-8859-7
28598  iso-8859-8
28599  iso-8859-9
28603  iso-8859-13
28605  iso-8859-15
29001  x-Europa
38598  iso-8859-8-i
50220  iso-2022-jp
50221  csISO2022JP
50222  iso-2022-jp
50225  iso-2022-kr
50227  x-cp50227
51932  euc-jp
51936  EUC-CN
51949  euc-kr
52936  hz-gb-2312
54936  GB18030
57002  x-iscii-de
57003  x-iscii-be
57004  x-iscii-ta
57005  x-iscii-te
57006  x-iscii-as
57007  x-iscii-or
57008  x-iscii-ka
57009  x-iscii-ma
57010  x-iscii-gu
57011  x-iscii-pa
65000  utf-7
65001  utf-8

2015-09-05

LINQPad強制不升級

LINQPad軟件本身強迫自動升級至最新版本,沒有任何設定選項能取消AutoUpdate。依官方文件問答,若不想升級的話,必須設定啟動程式參數-noupdate,以下是建立「LINQPad NoUpdate Loader.bat」的內容:

@echo off
start /b LINQPad.exe -noupdate

若不幸已自動升級了,就算把舊版本檔案蓋回來,啟動時仍會被蓋新檔。原來LINQPad自動升級的新版本下載會放在「C:\ProgramData\LINQPad\」目錄裏,它一比對到舊版本,就會蓋檔,因此刪除此目錄就可避免強迫升級。

LINQPad Logo

2015-09-02

顯示Exception錯誤的詳細資訊

為了顯示程序Exception例外錯誤的資訊,直覺會使用ex.Message訊息,但它其實不夠詳細,以下列出三種錯誤資訊的差別。

Exception Code Snippet

發現ToString()就列出了最完整的錯誤資訊,所以內部偵錯階段,請直接使用ex.ToString()函式吧!

Exception Msg