wordpress外掛開發 簡易綠界API串接結合自己開發的外掛(進階版+不須另外建立頁面)

閱讀本篇前建議先閱讀

重點概念

1.新建一個class(要被外掛主檔引入)

2.在該支程式__construct()內做網址判斷

這邊假設我希望綠界回傳給我的網址是{Mywebsite_base_Url}/ecpayverify
我的判斷就會如下:

if(strpos($_SERVER['REQUEST_URI'], '/ecpayverify/')===0||strpos($_SERVER['REQUEST_URI'], '/ecpayverify/')>0){

}

3.將要處理的邏輯寫在裡面,處理完後記得要"exit();"避免回傳給綠界資訊時產生多於的程式碼,以下附上範例

<?php
if(!class_exists('onlineScheduleTrans')):

class onlineScheduleTrans{
	public static $_instance=NULL;
	function __construct(){
		global $wpdb;

		add_action('wp_ajax_nopriv_buyService', array($this, 'buyService'));
		add_action('wp_ajax_buyService', array($this, 'buyService'));

		if(strpos($_SERVER['REQUEST_URI'], '/ecpayverify/')===0||strpos($_SERVER['REQUEST_URI'], '/ecpayverify/')>0){
			global $wpdb;
			require_once onlineSchedule_DIR.'/SDK/ECPay.Payment.Integration.php';
			define( 'ECPay_MerchantID', '3145753' );
			define( 'ECPay_HashKey', 'kUMKO4xa8YHtgc1B' );
			define( 'ECPay_HashIV', 'CSZqL9EXECkOMlUt' );
			 
			// 重新整理回傳參數。
			$arParameters = $_POST;
			foreach ($arParameters as $keys => $value) {
			    if ($keys != 'CheckMacValue') {
			        if ($keys == 'PaymentType') {
			            $value = str_replace('_CVS', '', $value);
			            $value = str_replace('_BARCODE', '', $value);
			            $value = str_replace('_CreditCard', '', $value);
			        }
			        if ($keys == 'PeriodType') {
			            $value = str_replace('Y', 'Year', $value);
			            $value = str_replace('M', 'Month', $value);
			            $value = str_replace('D', 'Day', $value);
			        }
			        $arFeedback[$keys] = $value;
			    }
			}
			 
			// 計算出 CheckMacValue
			$CheckMacValue = ECPay_CheckMacValue::generate( $arParameters, ECPay_HashKey, ECPay_HashIV );
			// 必須要支付成功並且驗證碼正確
			if ( $_POST['RtnCode'] =='1' && $CheckMacValue == $_POST['CheckMacValue'] ){
			    // 要處理的程式放在這裡,例如將線上服務啟用、更新訂單資料庫付款資訊等
			    $MerchantTradeNo=$_POST["MerchantTradeNo"];
			    $RtnCode=$_POST["RtnCode"];
			    $TradeAmt=$_POST["TradeAmt"];
			    $CheckMacValue=$_POST["CheckMacValue"];
			    //if success do something
			}
			else{
				//if fail do something
			}
			// 接收到資訊回應綠界
			echo '1|OK';
			exit();
		}
	}

	public static function instance(){
		if(is_null(self::$_instance))self::$_instance=new self();
		return self::$_instance;
	}

}

endif;

這個寫法是正規外掛的寫法,比起基礎版可以省去建立頁面的麻煩,如果本篇教學有幫到你記得幫我點個讚~

(分享) windows 批次(batch)檔 – 將指定格式的檔案編碼轉成 utf-8

各位在做案子的時候不知道有沒有遇過一種狀況,按照廠商方的需求開發完程式了,卻因為要處理的檔案編碼的問題導致又得增加額外的工作量,想要簡單的上網查個現成的轉編碼方式直接套用又總是遇到各種問題,因為每個語言讀入和寫出檔案都有各自的處理方式,所以常常導致最後執行完的結果往往跟想像中的不一樣?

這個是我自己後來最常用的解決方法,只要在自己原本要執行的程式執行前先呼叫此執行檔進行要處理的文件編碼,即可正常執行,這邊提供兩個版本一個是轉成UTF-8 with BOM,一個是轉成utf-8,裡面都有文字檔可以設定要轉檔的路徑(循環往下讀入)及要轉檔的檔案格式

如果這邊文章讓你少繞了很多路或是您覺得好用的話動動小手點讚一下給個支持吧!

轉成 UTF8( no BOM ) :
https://drive.google.com/file/d/1ycanhBFOS5XcB1L0_m7aKtUMk-rLn2mN/view?usp=sharing

轉成 UTF8( with BOM ) :
https://drive.google.com/file/d/1ycanhBFOS5XcB1L0_m7aKtUMk-rLn2mN/view?usp=sharing

WordPress外掛提交流程

  1. 前往 https://wordpress.org/plugins/developers/
  2. 點選 Submit your plugin for review

3.上傳你開發好的外掛zip檔
※外掛除了可運行的程式還會要求在根目錄要有readme.txt※
如果不清楚格式可到 https://generatewp.com/plugin-readme/ 自動生成
若要在描述的部分新增可點選的網址請按照此格式,不然會被當成純文字處理:
[要顯示的名稱](網址)
example: [Google](https://www.google.com/)

提交之後就等待wordpress他們的人員幫你審查,審查完後他們會寄信跟你說有那些問題,修改完後你再以github或是google雲端的方式告知他們程式碼已修正就好,反正就是能讓他們載的到你的程式就OK,接下來就是修正->審核->修正->審核的過程

以下講講我自己外掛被審出的需要修改的地方,有些真的是沒碰過不會發現有問題,但我覺的wordpress方也蠻用心地都有明確指出哪邊有哪個類型的錯誤,也有說明錯誤在哪支程式哪一行

插件名稱問題

大意是說你一開始取的外掛名稱不符合規範,他們會給你一個他們建議的名稱,照著改就好,記得要改readme.txt跟你外掛主代碼的外掛描述(如下圖)

插件包含多餘資料夾

開發的時候如果我們有引用第三方套件,常常都會將整包檔案放進程式裡,但這接第三方套件裡常常都包含如demo,example,doc等外掛並不需要使用到的部分,wordpress會要求您將這些非必要的部分移除外掛

插件呼叫遠程文件

開發外掛的時候為求方便有時候會直接call CDN , wordpress會要求可以下載到本地端做呼叫的檔案盡量下載到本地端,真的沒辦法本地呼叫的(如:googlemap相關的套件),可以允許遠端呼叫

插件必須驗證數據

我猜是因為安全性的考量,wordpress會要求您的後端程式碼在接收前端的數據時要做過驗證,這部分一開始我搞不太懂,想說我有對數據做檢查他怎麼還是回信說有問題
https://developer.wordpress.org/plugins/security/securing-input/
wordpress有提供蠻多現成的函式來讓開發者做數據驗證及清理,如果你接收的資料想要自定義(簡單來講不是像email這種有標準格式的),使用GET或POST接收前端數據的時候建議就使用sanitize_text_field這個函式,他主要是可以過濾使用者惡意插入的html tag

有興趣的可以自行搜尋: XSS攻擊

插件中含有通用的函式名稱

簡單來說就是你外掛中的function取的名字太通用了不夠有區別性,因wordpress上會同時掛載多個外掛,所以官方要確保你function的名字獨特才不會跟別的外掛相互影響

全部修正完之後官方就會給你一個svn網址讓你更新跟管理你的外掛,要進行第一次上傳之後你的外掛才會變為公開,這點請注意,至於如何使用svn就留待下一篇詳解吧!

wordpress本機基礎測試環境架設

開發外掛的過程一定會遇到很多不可預期的狀況,如果是接案的狀況再更改設定或外掛不小心玩壞了要修復也是件挺令人擔心的事情,所以這篇主要教大家如何在本地端架出wordpress的環境,出了問題也能夠比較好理解問題在哪哦!

首先先下載XAMPP

https://www.apachefriends.org/zh_tw/download.html

下載完之後進行安裝,預設安裝完的路徑會在C:\xampp

xampp下有個資料夾叫做htdocs是網站的根目錄

在htdocs下放一包wordprss(記得解壓縮)

https://drive.google.com/file/d/1XxxI75FTbDfuiOiX-cYWnnMu0emns3TY/view?usp=sharing

放好之後打開xampp程式

打開Apache跟MySQL

按下MySQL右邊的Admin
先建一個要給wordpress用的資料庫

打開網頁,輸入 http://localhost/wordpress/ ,會出現這個畫面

點擊開始安裝

資料庫名稱填上剛剛建的資料庫名稱,然後設定使用者名稱跟密碼,xampp的mysql預設的帳號密碼是root跟空白,基本上主機位置跟資料表前置詞可以用預設就好,輸入無誤後按傳送

點選執行安裝程式

輸入完畢後點選安裝Wordpress

完成啦!!!可以開始盡情的玩囉~

如果真的遇到玩壞了不知道該怎麼修復的情況,只要把wordpress刪掉重新安裝一次就可以了

wordpress外掛開發 簡易綠界API串接結合自己開發的外掛

架構上開發主要分為三個部分

1.主要處理使用者購買物品的function ,這個function主要的邏輯是將使用者購買的東西資料送往綠界的網址

function ,這個function主要的邏輯是將使用者購買的東西資料送往綠界的網址

2.放在SDK(我個人習慣)的ECPay.Payment.Integration.php(綠界提供的SDK),1跟3步驟的過程中都需要引入此檔案,其中這支程式的 static function CheckOut 要進行修改,要在送出此筆交易時紀錄該筆訂單的資訊

下載網址 :

https://drive.google.com/file/d/1b_kac8bhpClNAU19kwtI6i_CmmIW1RZF/view?usp=sharing

3.綠界交易結果的回傳頁面,做法就是將回傳頁面建立成模板,新建一個頁面當回傳頁面,可以自訂網址

主要的function


function MPBuy(){
		include MP_DIR.'/SDK/ECPay.Payment.Integration.php';

		global $wpdb;
		$ID=CheckLogin();//檢查是否登入
		//這邊先以單個商品購買作範例
		//若有多個改用array傳入即可
		$pointNum=$_POST["pointnum"];//商品數量

		$pointCost=$_POST["pointcost"];//商品價格

		//這邊給個建議,如果擔心程式安全性問題,建議這邊傳入商品ID,再去資料庫抓出當前價格

		if($pointNum==0||$pointCost==0){
			echo "<script>alert('購買失敗!');</script>";
			exit();
		}
		$pointSum=$_POST["pointnum"]*$_POST["pointcost"];
		$pointrent=ceil_dec((int)$pointSum*0.0275,0);

		if($pointrent<15){
			$pointrent=15;
		}
		//綠界那邊好像是不會計算手續費,所以這部份得要自己依據需求將手續費列入
		//不然手續費的部分就變成自行吸收了
		//這邊只是粗略的計算方式,想知道詳細可以去閱讀綠界的API說明

		try {

			$obj = new ECPay_AllInOne();

			//服務參數
			$obj->ServiceURL  = "https://payment.ecpay.com.tw/Cashier/AioCheckOut/V5";  //服務位置
			$obj->HashKey     = '$HashKey' ;                                          //請自行帶入ECPay提供的HashKey
			$obj->HashIV      = '$HashIV' ;                                          //請自行帶入ECPay提供的HashIV
			$obj->MerchantID  = '$MerchantID';                                                  //請自行帶入ECPay提供的MerchantID
			$obj->EncryptType = '1';                                                          //CheckMacValue加密類型,請固定填入1,使用SHA256加密


			//基本參數(請依系統規劃自行調整)
			$MerchantTradeNo = 'Myshop'.time() ;
			//這邊是序號,我自己習慣是網站名稱+timestamp
			$MerchantTradeDate = date('Y/m/d H:i:s');
			$obj->Send['ClientBackURL']="$消費者付款完畢跳轉的網址";
			//$obj->Send['ReturnURL']         = "http://www.ecpay.com.tw/receive.php" ;     //付款完成通知回傳的網址
			$obj->Send['ReturnURL']         = "$付款完成我方處理綠界回傳資訊的網址" ;
			$obj->Send['MerchantTradeNo']   = $MerchantTradeNo;                           //訂單編號
			$obj->Send['MerchantTradeDate'] = $MerchantTradeDate;                        //交易時間
			$obj->Send['TotalAmount']       = $pointSum+$pointrent;                        //交易金額
			$obj->Send['TradeDesc']         = "購買XXX" ;                           //交易描述
			$obj->Send['ChoosePayment']     = ECPay_PaymentMethod::ALL ;                  //付款方式:全功能

			//訂單的商品資料
			array_push($obj->Send['Items'],array('Name' => "XXX " , 'Price' => (int)$pointCost,'Currency' => "元", 'Quantity' => (int)$pointNum, 'URL' => "dedwed"),
array('Name' => "手續費", 'Price' => (int)$pointrent,'Currency' => "元", 'Quantity' => 1, 'URL' => "dedwed"));
			$sql = "INSERT INTO {$wpdb->prefix}coserorder (`MerchantTradeNo`,`orderTime`,`orderFee`,`orderUserId`) VALUES ('".$MerchantTradeNo."','".$MerchantTradeDate."','".$pointrent."','".$ID."')";
			$intReturn=$wpdb->query($sql);
			if($intReturn==0){
				echo "<script>alertify.log('購買失敗!');</script>";
				exit();
			}

			# 電子發票參數
			/*
        $obj->Send['InvoiceMark'] = ECPay_InvoiceState::Yes;
        $obj->SendExtend['RelateNumber'] = "Test".time();
        $obj->SendExtend['CustomerEmail'] = 'test@ecpay.com.tw';
        $obj->SendExtend['CustomerPhone'] = '0911222333';
        $obj->SendExtend['TaxType'] = ECPay_TaxType::Dutiable;
        $obj->SendExtend['CustomerAddr'] = '台北市南港區三重路19-2號5樓D棟';
        $obj->SendExtend['InvoiceItems'] = array();
        // 將商品加入電子發票商品列表陣列
        foreach ($obj->Send['Items'] as $info)
        {
            array_push($obj->SendExtend['InvoiceItems'],array('Name' => $info['Name'],'Count' =>
                $info['Quantity'],'Word' => '個','Price' => $info['Price'],'TaxType' => ECPay_TaxType::Dutiable));
        }
        $obj->SendExtend['InvoiceRemark'] = '測試發票備註';
        $obj->SendExtend['DelayDay'] = '0';
        $obj->SendExtend['InvType'] = ECPay_InvType::General;
        */


			//產生訂單(auto submit至ECPay)
			$obj->CheckOut();

		} catch (Exception $e) {
			echo $e->getMessage();
		} 
	}

綠界提供的SDK

接下來說明ECPay.Payment.Integration.php我有做修改的部分

static function CheckOut($target = "_self",$arParameters = array(),$arExtend = array(),$HashKey='',$HashIV='',$ServiceURL=''){
		global $wpdb;
	 	$MerchantTradeNo=$arParameters['MerchantTradeNo'];
		$MerchantTradeDate=$arParameters['MerchantTradeDate'];
        $arParameters = self::process($arParameters,$arExtend);
        //產生檢查碼
        $szCheckMacValue = ECPay_CheckMacValue::generate($arParameters,$HashKey,$HashIV,$arParameters['EncryptType']);
		//在資料表新增一筆訂單號與檢查碼
		$sql = "update {$wpdb->prefix}order set `szCheckMacValue`='".$szCheckMacValue."' where MerchantTradeNo='".$MerchantTradeNo."'";
    	$intReturn=$wpdb->query($sql);
		if($intReturn==0){
			echo "<script>alertify.log('購買失敗!');</script>";
			exit();
		}

        //生成表單,自動送出
        $szHtml = parent::HtmlEncode($target, $arParameters, $ServiceURL, $szCheckMacValue, '') ;
        echo $szHtml;
        exit;
    }

其中以下這段是我新增進去的,可依個人開發需求做調整,主要做的事情是記錄下訂單的資訊跟檢查碼,記得最上面要加global $wpdb; , 不然無法使用wp的資料庫哦!

$sql = "update {$wpdb->prefix}order set `szCheckMacValue`='".$szCheckMacValue."' where MerchantTradeNo='".$MerchantTradeNo."'";
    	$intReturn=$wpdb->query($sql);
		if($intReturn==0){
			echo "<script>alertify.log('購買失敗!');</script>";
			exit();
		}

綠界交易結果的回傳頁面

用模板的方式新建一個wp頁面,可以另外自訂網址,程式碼如下:

<?php
/*
Template Name: ECPay Template
*/
//要加這個註解wp框架才知道這支程式是模板
global $wpdb;
require_once dirname(__FILE__).'/ECPay.Payment.Integration.php';
define( 'ECPay_MerchantID', '$ECPay_MerchantID' );
define( 'ECPay_HashKey', '$ECPay_HashKey' );
define( 'ECPay_HashIV', '$ECPay_HashIV' );
 
// 重新整理回傳參數。
$arParameters = $_POST;
foreach ($arParameters as $keys => $value) {
    if ($keys != 'CheckMacValue') {
        if ($keys == 'PaymentType') {
            $value = str_replace('_CVS', '', $value);
            $value = str_replace('_BARCODE', '', $value);
            $value = str_replace('_CreditCard', '', $value);
        }
        if ($keys == 'PeriodType') {
            $value = str_replace('Y', 'Year', $value);
            $value = str_replace('M', 'Month', $value);
            $value = str_replace('D', 'Day', $value);
        }
        $arFeedback[$keys] = $value;
    }
}
 
// 計算出 CheckMacValue
$CheckMacValue = ECPay_CheckMacValue::generate( $arParameters, ECPay_HashKey, ECPay_HashIV );
// 必須要支付成功並且驗證碼正確
if ( $_POST['RtnCode'] =='1' && $CheckMacValue == $_POST['CheckMacValue'] ){
    // 
    // 要處理的程式放在這裡,例如將線上服務啟用、更新訂單資料庫付款資訊等
    // 
    $MerchantTradeNo=$_POST["MerchantTradeNo"];
    $RtnCode=$_POST["RtnCode"];
    $TradeAmt=$_POST["TradeAmt"];
    $CheckMacValue=$_POST["CheckMacValue"];
}
else{
}
 
// 接收到資訊回應綠界
echo '1|OK';
?>

然後在class_myplugin-main.php的__construct內加上,這個方法要記得在開發完後的ECPay-template.php要以ECPay-template.txt的方法儲存,不然複製到目的資料夾時只會是執行結果而非內容,ECPay.Payment.Integration.txt則是txt跟php檔都得各存一份,一份是留給外掛function呼叫,一份是給複製完的模板頁面使用

copy(MP_DIR."/APItemplates/ECPay-template.txt", get_template_directory()."/ECPay-template.php");
		//get_template_directory()取得當前主題路徑
		//記得原頁面開發完要轉成txt
		//phpA 複製給 phpB的話 phpB的內容只會是phpA執行完的的結果(非內容)

		copy(MP_DIR."/SDK/ECPay.Payment.Integration.txt", get_template_directory()."/ECPay.Payment.Integration.php");
		//複製綠界SDK到相對路徑下

以方法二撰寫外掛要記得到wordpress的頁面新增一個頁面,模板選擇ECPay Template,就可以自訂處理綠界回傳值的網址啦!
記得網址設定完後記得修改第一步驟內的回傳網址
看到這邊如果對以上程式的相對路徑有疑問的可以直接載這包來看,會比較好理解!
記得這包是不能直接跑的哦,如果要測試新增一個頁面使用ajax呼叫function即可

外掛結合簡易綠界程式載點

https://drive.google.com/file/d/1sh0ysyTMbi_LfN6Pn3NqJ9TNXOdgoiiS/view?usp=sharing

wordpress外掛開發 讓外掛自動新建所需資料表

寫一個外掛的重點其中之一,讓外掛能自動建立本身所需的資料表是很重要的,如果是要在複數個網站安裝自己寫的外掛這個部分就更加重要
下面就讓我們來看看要如何做到讓外掛自動建立資料表

主要步驟如下:

首先在class的function __construct內加入

if(!defined('ABSPATH'))exit;
$this->CreateTable();
//自動新增所需資料表

CreateTable就是待會我們要建立資料表的function,將上述語法放在 function __construct 內是為了要讓其自動執行,達到自動建立的效果

接下來讓我們看CreateTable()內的內容

function CreateTable(){
	global $wpdb;

	require_once(ABSPATH.'wp-admin/includes/upgrade.php');
    //要更新資料表就得require這支wp程式
	$charset_collate=$wpdb->get_charset_collate();//取得資料表的預設編碼
	//如果有中文就設成utf8避免亂碼

	/*begin of 有多個table就要寫多個區塊*/
	$table_name=$wpdb->prefix.'_user_register';
	if($wpdb->get_var("SHOW TABLES LIKE '$table_name'")!=$table_name){
		$sql="CREATE TABLE `{$wpdb->prefix}_user_register` (
		  `ID` int(11) NOT NULL AUTO_INCREMENT,
		  `名字` varchar(10) NOT NULL,
		  `密碼` varchar(100) NOT NULL,
		  `信箱` varchar(50) NOT NULL,
		  `手機` varchar(10) NOT NULL PRIMARY KEY
		 )$charset_collate;";
		 //如果要限制一人只能註冊一個帳號,建議把手機設為primary key並且在註冊流程加上手機認證
		 dbDelta($sql);
		/*end of 有多個table就要寫多個區塊*/
	}
}

邏輯上簡單來說是先用 SHOW TABLES LIKE ‘$table_name’ 來確認是否已存在同名稱的資料表,若不存在就使用dbDelta這個函式來建立該資料表

以簡易框架教學那篇的架構來說的話,如果資料表少我就會放在main.php內