WooCommerce でライセンスキーを発行するプラグイン及びそれを Web サービスで利用するためのコード

題名が長くなりましたが、以下のプラグインを自分のサービスで利用するために開発しました。

ウェブサービス用の WooCommerce 商品のライセンスキーを生成します。
(0) 最終更新: 2週間 前
126ダウンロード 検証済み: 5.8.1
ダウンロード: リリース: 2021-09-23

プラグイン自体は可能な限りシンプルにしていて、以下の機能に絞っています。

  • 購入されたライセンスキーを暗号化して Rest API で出力
  • 商品にライセンスキーを付与するかのチェック
  • ライセンスキーの有効期日の指定

後は、購入したユーザーが、Web サービス上でメールで届いたライセンスキーを入力すると、それを復号化して検証するという仕組みです。それについては、Web サービス上でコードを書く必要があります。暗号化は、OpenSSL の共通パスフレーズを用いています。コードに関しては、WordPress の管理画面上での PHP と、React で以下に記していきますが、Rest API と、OpenSSL を利用しているので、他の言語やシステムでも別のコードを書けば使えると思います。その辺りに詳しい方は、チャレンジしてみてください。

動作

ライセンスキー販売サイトの商品の設定

ライセンスキーを発行にチェックし、有効期限を決めます。有効期限を過ぎると、REST API への暗号化されたライセンスキーの出力は無くなります。商品は、バーチャルにチェックを入れます。

ライセンスキー販売サイトのプラグイン側の設定

共通パスフレーズを決めます。これは流出しないように大切に保管してください。また、プラグインの設定画面上で、REST API の URL を知る事ができます。この2点は、後で PHP と React 上に書き込みます。

Web サービス側の PHP コード

子テーマの、functions.php に書く前提で記していきます。

ライセンスキーを入力するためのメニューページを作成するコード

  • 管理画面に、Test License というページを作成
  • ページには、React を利用するので、id 属性を指定して、echo するのみ
/** ==================================================
 * Add Menu for Test license
 *
 * @since 1.00
 */
function add_pages() {
	add_menu_page(
		'Test License',
		'Test License',
		'edit_posts',
		'TestLicense',
		'credit_page'
	);
}
add_action( 'admin_menu', 'add_pages' );

/** ==================================================
 * Credit
 *
 * @since 1.00
 */
function credit_page() {

	echo '<div id="test-license-page"></div>';

}

メニューページに読み込むスクリプト用のコード

  • 子テーマ内に、test-license というフォルダを作成し、その下の dist フォルダ内に、React の build ファイルを置くためのコード
/** ==================================================
 * License Page Scripts
 *
 * @param string $hook_suffix  hook_suffix.
 * @since 1.00
 */
function license_page_scripts( $hook_suffix ) {

	if ( 'toplevel_page_TestLicense' !== $hook_suffix ) {
		return;
	}

	$asset_file = include( get_stylesheet_directory() . '/test-license/dist/test-license.asset.php' );

	wp_enqueue_style(
		'test-license-style',
		get_stylesheet_directory_uri() . '/test-license/dist/test-license.css',
		array( 'wp-components' ),
		'1.0.0'
	);

	wp_enqueue_script(
		'test-license',
		get_stylesheet_directory_uri() . '/test-license/dist/test-license.js',
		$asset_file['dependencies'],
		$asset_file['version'],
		true
	);

}
add_action( 'admin_enqueue_scripts', 'license_page_scripts', 10, 1 );

REST API を出力し、入力されたライセンスキーを検証するコード

  • 権限は以下の場合は編集者権限
  • license_api_verify 内の検証コード内に、復号化のための共通パスフレーズを記す
  • 検証して、入力されたライセンスキーと、プラグインの入っているライセンスキー販売サイトの REST API を復号化したものが一致していれば、ユーザーオプションテーブルの test_licensed を true にする
/** ==================================================
 * Register Rest API
 *
 * @since 1.00
 */
function register_rest() {

	register_rest_route(
		'test/license_api',
		'/token',
		array(
			'methods' => 'POST',
			'callback' => 'license_api_verify',
			'permission_callback' => 'editor_rest_permission',
		)
	);

}
add_action( 'rest_api_init', 'register_rest' );

/** ==================================================
 * Rest Permission for editor
 *
 * @since 1.00
 */
function editor_rest_permission() {

	return current_user_can( 'edit_posts' );

}

/** ==================================================
 * License Rest API verify
 *
 * @param object $request  changed data.
 * @since 1.00
 */
function license_api_verify( $request ) {

	$args = json_decode( $request->get_body(), true );

	$input_license_key = $args['license_key'];
	$get_license_api_arr = $args['license_api'];
	foreach ( $get_license_api_arr as $key => $value ) {
		/* 共通パスフレーズは、プラグイン側で指定したもの。以下の場合は、'm#xs@%5NhZlR' */
		$get_license_key = openssl_decrypt( $value['open_key'], 'aes-256-cfb', 'm#xs@%5NhZlR', 0, openssl_cipher_iv_length( 'aes-256-cfb' ) );
		if ( $get_license_key === $input_license_key ) {
			/* ライセンスが一致した時の処理 */
			update_user_option( get_current_user_id(), 'test_licensed', true );
		}
	}
	$args['licensed'] = get_user_option( 'test_licensed', get_current_user_id() );

	return new WP_REST_Response( $args, 200 );

}

Web サービス側の React コード

LicenseCheck コンポーネント

  • Fetch で販売サイトの REST API を取得
  • apiFetch で Web サービス側で、ライセンスキーを入力したものと、Fetch で取得した暗号化されたライセンスキーを php 側と通信
import './license-check.css';

import apiFetch from '@wordpress/api-fetch';

import { Button, TextControl } from '@wordpress/components';

import {
	useState,
	useEffect
} from '@wordpress/element';

const LicenseCheck = ( props ) => {

	const [ paidFlag, updatePaidFlag ] = useState( false );
	const [ paidText, updatePaidText ] = useState( '' );

	const [ currentLicense, updateCurrentLicense ] = useState( '' );
	const [ licenseApi, updateLicenseApi ] = useState( {} );

	const items_licensed = [];
	if ( ! paidFlag ) {
		items_licensed.push(
			<div>
				<hr />
				<h3>{ props.license_text }</h3>
				<p className="description">{ props.purchase_description }</p>
				<div>
					<Button
						className="button button-large"
						href={ props.purchase_site }
						target="_blank"
					>
					{ props.purchase_text }
					</Button>
				</div>
				<div className="license_key_input_line">
					{ props.license_text_input }
					<TextControl
						className = "license_key_input"
						value = { currentLicense }
						onChange = { ( value ) => updateCurrentLicense( value ) }
					/>
				</div>
			</div>
		);
	}

	useEffect( () => {

		fetch( props.url )
		.then( response => response.json() )
		.then( data => {
			//console.log( data );
			updateLicenseApi( data );
		} );

		apiFetch( {
			path: props.path,
			method: 'POST',
			data: {
				license_key: currentLicense,
				license_api: licenseApi,
			}
		} ).then( ( response ) => {
			//console.log( response );
			if ( response['licensed'] ) {
				updatePaidFlag( true );
				updatePaidText( props.paid_text );
			} else {
				updatePaidFlag( false );
				updatePaidText( props.no_paid_text );
			}
		} );

	}, [ currentLicense ] );

	return (
		<div className="wrap">
			<h2>{ props.title }</h2>
			<h3>{ paidText }</h3>
			{ items_licensed }
		</div>
	);

};

export default LicenseCheck;

コンポーネント用の CSS

.license_key_input {
	width: 200px;
}
.license_key_input_line {
	display: inline-flex;
}

メインのコード

  • コンポーネントを読み込む。コンポーネントの属性に各種値を入力。
  • 属性
    • title : タイトル
    • license_text : タイトル2
    • purchase_description : 購入の説明
    • purchase_text : 購入ボタンの文字
    • purchase_site : ライセンスキー商品販売ページの URL
    • license_text_input : ライセンスキー入力フォームの左側のテキスト
    • paid_text : ライセンスされている事を示すテキスト
    • no_paid_text : ライセンスされていない事を示すテキスト
    • url : ライセンス販売サイトの REST API の URL(プラグインの設定画面に記してあるもの)
    • path : Web サービス側の REST API のパス(PHP に記したパス)
import './test-license.scss';

import { render } from '@wordpress/element';

import LicenseCheck from './components/license-check';

const TestLicenseView = () => {

	return (
		<div className="wrap">
			<LicenseCheck
				title = { 'ライセンスチェックテスト' }
				license_text = { 'ライセンスキー' }
				purchase_description = { '以下のショップで購入したライセンスキーを入力すると制限が解除されます。' }
				purchase_site = 'https://shop-jp.riverforest.test/product/license'
				purchase_text = { '購入' }
				license_text_input = { 'ライセンスキー入力' }
				paid_text = { 'ライセンス済み' }
				no_paid_text = { '未ライセンス' }
				/* REST API by Simple License Key for WooCommerce */
				url = 'https://shop-jp.riverforest.test/wp-json/rf/slk-woo-open-key_api/token'
				/* Web service REST API */
				path = 'test/license_api/token'
			/>
		</div>
	);

};

render(
	<TestLicenseView />,
	document.getElementById( 'test-license-page' )
);

メインの CSS

#test-license-page {
}

ダウンロード

コードを子テーマ毎一式以下に置きました。

この記事を書いた人