カテゴリー
Laravel PHP

Laravel Migrationでデータベーステーブルにカラムを追加

カラムを追加する場合

// below command will generate a new migration file
php artisan make:migration add_omit_root_to_chords --table="chords"

// inside the migration file, write as below to add new columns

public function up()
{
        Schema::table('chords', function (Blueprint $table) {
            $table->boolean("omit_root")->nullable();
            $table->integer("genre")->nullable();
        });
    }
    //  ~~~~~~~~
}

カラムのタイプを変更する場合

// you need doctrine/dbal
composer require doctrine/dbal

// to create migration 
php artisan make:migration change_omit_root_type --table="chords"

// inside migration
Schema::table('chords', function (Blueprint $table) {
    $table->integer("omit_root")->change();
});
カテゴリー
JavaScript PHP

PHP変数をJavaScriptで扱うには(そしてその逆も)

一旦Dom Elementに値を保持させて、その後JavaScriptでその値をゲットする方法が良さそう。このリンク

カテゴリー
PHP

AWSにLEMP環境を構築する

この手順を参考に設定をする

Laravelの導入はこの手順

laravelプロジェクトをgithubからcloneしたら、以下を実施し、APP_DEYを生成する。

composer update
カテゴリー
Laravel PHP StoreKit2

StoreKitのTransaction HistoryをPHP LaravelでDecodeする

function verifyPurchaseAppleStoreKit2(Request $request) {

  $sandbox = true;

  $transactionID = $request->query("transactionID");

  $url = "https://api.storekit.itunes.apple.com/inApps/v1/history/$transactionID";
  if ($sandbox) {
    $url = "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/history/$transactionID";
  }

  $handle = curl_init($url);
  $token = $this->generateJWTForSigningApplePurchase();
  curl_setopt($handle, CURLOPT_HTTPHEADER, ["Authorization: Bearer $token"]);
  curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
  // response includes multiple signedTransactions
  $response = curl_exec($handle);
  $httpcode = curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
  curl_close($handle);
  
  // getting a json object from the $response
  $json = json_decode($response, false);
  return $json;
}

from the above function, you below result:

{
  "revision":"1656919939000_2000000096041637",
  "bundleId":"com.yourdomain.yourappname.test",
  "environment":"Sandbox",
  "hasMore":false,
  "signedTransactions":[LIST OF SIGNED TRANSACTION]
}

each signedTransaction contains below information.

{
  "transactionId":"SOME NUMBER",
  "originalTransactionId":"SOME NUMBER",
  "bundleId":"com.yourdomain.yourappname.test",
  "productId":"com.yourdomain.yourappname.test.in_app_product_name",
  "purchaseDate":1644992734000,
  "originalPurchaseDate":1644992734000,
  "quantity":1,
  "type":"Non-Consumable",
  "inAppOwnershipType":"PURCHASED",
  "signedDate":1658031241903,
  "environment":"Sandbox"
}

so, you want to iterate over $json->signedTransactions, and find a match.

foreach($transactions as $transaction) {

}
use Carbon\Carbon;
use Lcobucci\JWT\Builder;
use Lcobucci\JWT\Signer\Key;
use Lcobucci\JWT\Signer\Ecdsa\Sha256;

public function generateJWTForSigningApplePurchase() {
  $signer = new Sha256();
  $key = env('JWT_SECRET');
  $privateKey = new Key($key);
  $time = Carbon::now()->timestamp;
  $token = (new Builder())
    ->issuedBy(env('ISSUER_ID'))
    ->issuedAt($time)
    ->expiresAt($time + (60 * 60))
    ->withClaim('bid', env('BUNDLE_ID'))
    ->withClaim('aud', "appstoreconnect-v1")
    ->withHeader('alg', 'ES256')
    ->withHeader('kid', env('KID'))
    ->withHeader('typ', 'JWT')
    ->getToken($signer, $privateKey);
  return $token->__toString();
}

Client-side code in Swift

func verify(id: String) {	
  let urlStringVerifyPurchase = "https://YOUR_DOMAIN.COM/verify/"
  Task {
    guard let verificationResult = await products.filter({ $0.id == id}).first?.currentEntitlement else { return }
    switch verificationResult {
      case .verified(let transaction):
        // your validation function
        validation(transaction: transaction)
	let queryItems = [URLQueryItem(name: "transactionID", value: "\(transaction.originalID)")]
	var urlComps = URLComponents(string: urlStringVerifyPurchase)!
	urlComps.queryItems = queryItems
	guard let url = urlComps.url else { return }
	let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
	  guard let data = data else {
	    print("no data")
	    return
	  }
	  do {
	    let decode = try JSONDecoder().decode(ServerVerifyPurchaseResponse.self, from: data)
	    if decode.code == 200 {						   
              self?.performDownload(id: id)
	    }
	  } catch {
	    print("error occurred when decoding verify purchase data: \(error)")
	  }
	}
	task.resume()
      case .unverified(let transaction, _):
        print(transaction)
    }
  }
}