2021年9月15日 星期三

Samba - Ubunta 和 Windows 之間的橋樑

 1. 先在 Ubunta 安裝 Samba 

sudo apt-get install samba


2. 設定 Samba 工作群組,要和Windows 相同

 vim /etc/samba/smb.conf

3. 設定要分享的資料夾,格式如下

[ShareName] 分享資料夾名稱

path = /var/usr/    分享資料夾路徑

read only = no    是否唯讀

create mask = 777     檔案遮罩

directory mask = 777    資料夾遮至

writable = yes    是否可寫入

4. 將 Samba 加進防火牆白名單

sudo ufw status verbose → 確認防火牆狀態

sudo ufw allow samba → 將 Samba 加進防火牆白名單

sudo ufw reload → 重啟防火牆

5. 重啟 Samba 讓他吃修改後的設定檔

sudo service smbd restart

6. 完成

2021年8月25日 星期三

AppsFlyer 廣告追蹤

 Apps Flyer 是一個多平台整合的廣告追蹤網站


Apps Flyer 相關

Android SDK 集成

iOS SDK 集成


Facebook 相關

Facebook 廣告配置指南

Facebook SDK

Facebook Dashboard



iOS 14.5 官方將廣告追蹤透明化

必須經過用戶同意方可進行追蹤

再設定 Facebook 追蹤時,

必須將事件管理工具 > 資料來源 > 設定 > 為 SKAdNetwork 設定應用程式事件 開啟

並編輯事件,才可以收到來自 iOS 的資料


Swift 

AppEvents.logEvent(AppEvents.Name.completedRegistration)


2021年8月24日 星期二

iOS 訂閱性商品是否可取得續訂資料

 可以透過 iOS 驗證收據中,取得最新的訂閱資料中查看 訂閱截止日

作法:

  1. iOS成功購買產品後,呼叫 Fetch the Receipt Data(備註1) 去取 Apple Store 收據
  2. 因為 Apple Store 收據 本身有經過加密,也因為安全性的問題,需由後端去與 Apple 伺服器溝通
  3. 承第二點可透過 POST MAN 進行測試實際用法參考 (備註2)
  4. 將第一點取得的加密字串傳給後端API
  5. 由後端去和APPLE伺服器溝通,取得解碼後的收據詳細資料(JSONObject)
  6. 儲存至DB

備註:
1. iOS 原生

if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, FileManager,default.fileExists(atPath: appStoreReceiptURL.path){
  do{
    let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
    let receiptString = receiptData.base64EncodedString(options: [])

    // 呼叫 API

  } catch {
    print("Couldn't read receipt data with error: " + error.localizedDescription)
  }
}

2. 驗證收據是否正確
正式驗證 API 網址
https://buy.itunes.apple.com/verifyReceipt
沙箱驗證 API 網址
https://sandbox.itunes.apple.com/verifyReceipt
資料來源:https://www.jianshu.com/p/d0ac8cec794b

3. 驗證資料的格式,必須自建為 JSONObject
https://developer.apple.com/documentation/appstorereceipts/requestbody

4.回傳的狀態碼
https://developer.apple.com/documentation/appstorereceipts/status

5. 後端

function setReceiptIos($arr){
		if(!isset($arr)){
			return 'ESY9001';
		} else if(empty($arr["receipt_data"])){
			return 'EPT0015';
		} else if(empty($arr["uid"])){
			return 'EPT0007';
		} else if(empty($arr["iap_type"])){
			return 'EPT0016';
		} else if(empty($arr["ptid"])){
			return 'EPT0004';
		}
		
		// get mydb
		$mydb = &$this->mydb;
		
		date_default_timezone_set("Asia/Taipei");
		
		function acurl($receipt_data, $sandbox=0){
			
			// SandBox , Official
			$secret = apple_connect_serect_key;
			$POSTFIELDS = array("receipt-data" => $receipt_data, 'password'=>$secret, "");
			$POSTFIELDS = json_encode($POSTFIELDS);
	 
			//正式購買地址 沙盒購買地址
			$url_buy     = "https://buy.itunes.apple.com/verifyReceipt";
			$url_sandbox = "https://sandbox.itunes.apple.com/verifyReceipt";
			$url = $sandbox ? $url_sandbox : $url_buy;
	 
			//簡單的curl
			$ch = curl_init($url);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_POST, 1);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $POSTFIELDS);
			$result = curl_exec($ch);
			curl_close($ch);
			return $result;
		}
		
		// 請求驗證
	    $html = acurl($arr["receipt_data"]);
		$data = json_decode($html, true);
		// 如果是沙盒資料 則驗證沙盒模式
	    if($data['status']=='21007'){
	        // 請求驗證
	        $html = acurl($arr["receipt_data"], 1);
	        $data = json_decode($html,true);
	        $data['sandbox'] = '1';
	    }
		
		 // 判斷是否購買成功
	    if(intval($data['status'])===0){
	        $result=array(
	            'status'  => $data['status'],
	            'message' => '購買成功'
	            );
	    }else{
			
	        $result=array(
	            'status'  => $data['status'],
	            'message' => '購買失敗'
	            );
				
			return $result;
	    }
		
		$latestPosition = 0;
		
		
		
		$purchase_date_ms = "0";
		
		$receipt = array();
		// latest_receipt_info 只會回傳可續訂的收據, 其他收據要在 IN_APP 裡面找
		if ($arr["iap_type"] == "subscription") {
			$receipt = $data["latest_receipt_info"];
		} 
		else {
			$receiptArr = $data["receipt"]["in_app"];
			$receiptArrLength = count($receiptArr);
			if ( $receiptArrLength > 0){
				for ($i=0; $i<$receiptArrLength; $i++){
					if ( (double) $receiptArr[$i]["purchase_date_ms"] > (double) $purchase_date_ms ){
						$purchase_date_ms = (double) $receiptArr[$i]["purchase_date_ms"]; 
						$latestPosition = $i;
					}
				}
			}
			
			$receipt = $receiptArr;
		}
		
		
		$transaction_id = 'NULL';
		if (isset($receipt[$latestPosition]["transaction_id"]) ){
			$transaction_id = "'".$receipt[$latestPosition]["transaction_id"]."'"; 
		}
		
		$product_id = 'NULL';
		if (isset($receipt[$latestPosition]["product_id"]) ){
			$product_id = "'".$receipt[$latestPosition]["product_id"]."'"; 
		}
		
		
		$purchase_date = 'NULL';
		if (isset($receipt[$latestPosition]["purchase_date"]) ){
			$purchase_date = "'".date("Y-m-d H:i:s",strtotime($receipt[$latestPosition]["purchase_date"]))."'"; 
		}
		
		$expires_date = 'NULL';
		if (isset($receipt[$latestPosition]["expires_date"]) ){
			$expires_date = "'".date("Y-m-d H:i:s",strtotime($receipt[$latestPosition]["expires_date"]))."'"; 
		}
		
		$cancellation_date = 'NULL';
		if (isset($receipt[$latestPosition]["cancellation_date"]) ){
			$cancellation_date = "'".date("Y-m-d H:i:s",strtotime($receipt[$latestPosition]["cancellation_date"]))."'"; 
		}
		
		$cancellation_reason = 'NULL';
		if (isset($receipt[$latestPosition]["cancellation_reason"]) ){
			$cancellation_reason = "'".$receipt[$latestPosition]["cancellation_reason"]."'"; 
		}
		
		$original_purchase_date = 'NULL';
		if (isset($receipt[$latestPosition]["original_purchase_date"]) ){
			$original_purchase_date = "'".date("Y-m-d H:i:s",strtotime($receipt[$latestPosition]["original_purchase_date"]))."'"; 
		}
		
		{ $query = "
		INSERT INTO `".PREFIX_TABLE_0."_".DB_NAME_1."`.`".PREFIX_TABLE_0."_history_user_ios_receipt` (
			`prefixid` ,
			`huirid` ,
			`uid` ,
			`transaction_id` ,
			`product_id` ,
			`purchase_date` ,
			`expires_date` ,
			`cancellation_date` ,
			`cancellation_reason` ,
			`original_purchase_date` ,
			`encode_string` ,
			`ptid` ,
			`seq` ,
			`switch` ,
			`insertt`,
			`modifyt`
		) VALUES (
			'".PREFIX_INDEX_0."',
			NULL,
			'".$arr["uid"]."',
			{$transaction_id},
			{$product_id},
			{$purchase_date},
			{$expires_date},
			{$cancellation_date},
			{$cancellation_reason},
			{$original_purchase_date},
			'".$arr["receipt_data"]."',
			'".$arr["ptid"]."',
			'10',
			'Y',
			NOW(),
			NOW()
		)";
		}
		
		
		// echo $query ; exit;
		if(!$mydb->query($query)) return 'ESY9002';
			
		return $result;
	}

2020年8月29日 星期六

PHP Session 使用範例說明

今天想用一個session來實現用戶登錄判斷,也算是對之前session的探究,查了下資料session的運行機制如下:


  session是服務器端的一種會話機制,當客戶端的請求服務器創建一個session時,服務器會先檢測該請求裡面是否包含一個惟一的sessionID,如果是,說明服務器已經為該用戶創建過session,只要按照該sesionID檢索出該用戶的session供用戶使用,如果沒有sessionID,服務器會為該用戶新建一個帶有唯一表示服sessionID的session。創建完成後,該sessionID會被服務器返回給客戶端,保存到客戶端本地。


  一般保存該session ID的機制是Cookie,但是由於Cookies可以被人為禁止,這就得保證Cookies被禁止之後,仍舊可以通過session進行會話,一般是通過url重寫進行,表現形式為http://...../xxx;jsessionid= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764,另一種是作為查詢字符串附加在URL後面,表現形式為http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764這兩種方式對於用戶來說是沒有區別的,只是服務器在解析的時候處理的方式不同,採用第一種方式也有利於把session id的信息和正常程序參數區分開來。

為了在整個交互過程中始終保持狀態,就必須在每個客戶端可能請求的路徑後面都包含這個session id。





另外是關於session失效的誤區:

  在談論session機制的時候,常常聽到這樣一種誤解“只要關閉瀏覽器,session就消失了”。其實可以想像一下會員卡的例子,除非顧客主動 對店家提出銷卡,否則店家絕對不會輕易刪除顧客的資料。對session來說也是一樣的,除非程序通知服務器刪除一個session,否則服務器會一直保 留,程序一般都是在用戶做log off的時候發個指令去刪除session。然而瀏覽器從來不會主動在關閉之前通知服務器它將要關閉,因此服務器根本不會有機會知道瀏覽器已經關閉,之所 以會有這種錯覺,是大部分session機制都使用會話cookie來保存session id,而關閉瀏覽器後這個 session id就消失了,再次連接服務器時也就無法找到原來的session。如果服務器設置的cookie被保存到硬盤上,或者使用某種手段改寫瀏覽器發出的 HTTP請求頭,把原來的session id發送給服務器,則再次打開瀏覽器仍然能夠找到原來的session。


  恰恰是由於關閉瀏覽器不會導致session被刪除,迫使服務器為seesion設置了一個失效時間,當距離客戶端上一次使用session的時間超過這個失效時間時,服務器就可以認為客戶端已經停止了活動,才會把session刪除以節省存儲空間。


好了,廢話說了一大堆,說session丟失的解決辦法吧:

1、session_start();應該盡量放置到頁面的頂部;

2、如果php.ini裡面沒有配置 session Autostart的話,每次會話之前,都得手動開啟session:session_start();

3、session是php裡面的超全局變量,跟$_GET,$_POST,$_SERVER一樣,所以使用的時候必須大寫:$_SESSION['username']=$username;

4、跨頁面傳遞示例:a.php頁面傳遞$_SESSION['username']到b.php:

a.php: 

<?php
session_start();
$username=$_POST['username'];
$_SESSION['username']=$username;
?>

b.php

<?php
session_start();
echo $_SESSION['username'];
?>

2019年8月27日 星期二

GitHub 上傳物件步驟

STEP.1 開啟 Git Bash
STEP.2 cd 至 你的目錄

STEP.3 echo "# Homeet" >> README.md -- 新增一個README.md 檔案

STEP.4 git init -- 初始化 git


STEP.5 git add . -- 新增所有檔案

STEP.6 git commit -m "提交記錄文字" -- 提交檔案

STEP.7 git push -u origin master -- 推取(上傳)至master 分支

STEP.8 完成

2019年6月10日 星期一

期末專題:RWD、版控


  1. 因每個人製作的網頁不同,為了讓整個網站整齊一致,所以統一網頁的寬度,並因平台不同,順便制定需使用RWD,對各平台撰寫CSS。
  2. 因為版本會有三個版本,目前是用GitLab+TortoiseGit來做版本控制
選用GitLabTortoiseGit的原因有兩個:
1. 它們有中文
2. GitLab的專案可以私人化

Android Studio IDE 錯誤

 :app:compile xxxxx JavaWithJavac FAILED An exception has occurred in the compiler (1.8.0_312). Please file a bug against the Java compiler ...