{"id":27850,"date":"2026-04-11T15:47:26","date_gmt":"2026-04-11T06:47:26","guid":{"rendered":"https:\/\/motoki-design.co.jp\/wordpress\/?p=27850"},"modified":"2026-09-26T12:37:00","modified_gmt":"2026-09-26T03:37:00","slug":"wordpress-plugin-gumroad-license-authentication","status":"publish","type":"post","link":"https:\/\/motoki-design.co.jp\/wordpress\/wordpress-plugin-gumroad-license-authentication\/","title":{"rendered":"WordPress\u2502\u81ea\u4f5c\u30d7\u30e9\u30b0\u30a4\u30f3\u306bGumroad\u30e9\u30a4\u30bb\u30f3\u30b9\u8a8d\u8a3c\u3092\u5b9f\u88c5\u3059\u308b\u65b9\u6cd5"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">WordPress\u306e\u6709\u6599\u30d7\u30e9\u30b0\u30a4\u30f3\u3092Gumroad\u3067\u8ca9\u58f2\u3059\u308b\u5834\u5408\u3001<strong>\u30e9\u30a4\u30bb\u30f3\u30b9\u8a8d\u8a3c<\/strong>\u306e\u4ed5\u7d44\u307f\u3092\u3069\u3046\u4f5c\u308b\u304b\u304c\u8ab2\u984c\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">EDD\uff08Easy Digital Downloads\uff09\u3084Lemon Squeezy\u306a\u3069\u306e\u30b5\u30fc\u30d3\u30b9\u3092\u4f7f\u3046\u65b9\u6cd5\u3082\u3042\u308a\u307e\u3059\u304c\u3001\u3053\u306e\u8a18\u4e8b\u3067\u306f<strong>Gumroad\u306e\u8ca9\u58f2\u6a5f\u80fd\uff0b\u81ea\u524d\u306eWordPress REST API\u30b5\u30fc\u30d0\u30fc<\/strong>\u3060\u3051\u3067\u5b8c\u7d50\u3059\u308b\u30b7\u30f3\u30d7\u30eb\u306a\u69cb\u6210\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<div class=\"wp-block-group has-border -border01\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<p class=\"wp-block-paragraph\">\u30c4\u30fc\u30eb\u3092\u8ca9\u58f2\u3067\u304d\u308bGumroad\uff08\u30ac\u30e0\u30ed\u30fc\u30c9\uff09\u3067\u306f\u8cfc\u5165\u3057\u305f\u30e6\u30fc\u30b6\u30fc\u3054\u3068\u306b\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u304c\u767a\u884c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3092\u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\uff08WordPress\u3092\u5165\u308c\u3066\u30d7\u30e9\u30b0\u30a4\u30f3\u3092\u5165\u308c\u308b\u3060\u3051\uff09\u3067\u3001\u5b9a\u671f\u7684\u306b\u81ea\u52d5\u8a8d\u8a3c\u3057\u3066\u30d7\u30e9\u30b0\u30a4\u30f3\u3092\u4f7f\u3048\u308b\u3088\u3046\u306b\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"812\" height=\"500\" src=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h14_18-812x500.png\" alt=\"Gumroad\u3067\u8cfc\u5165\u3057\u305f\u30e6\u30fc\u30b6\u30fc\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u3092\u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u3067\u5b9a\u671f\u7684\u306b\u81ea\u52d5\u8a8d\u8a3c\u3059\u308b\u4ed5\u7d44\u307f\u306e\u30a4\u30e1\u30fc\u30b8\u3002\" class=\"wp-image-27853\" srcset=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h14_18-812x500.png 812w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h14_18-382x235.png 382w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h14_18-768x473.png 768w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h14_18.png 924w\" sizes=\"(max-width: 812px) 100vw, 812px\" \/><\/figure>\n<\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">\u5916\u90e8\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u7ba1\u7406\u30b5\u30fc\u30d3\u30b9\u3078\u306e\u6708\u984d\u8cbb\u7528\u306f\u4e00\u5207\u4e0d\u8981\u3067\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u3053\u306e\u8a18\u4e8b\u3067\u5b9f\u88c5\u3059\u308b\u69cb\u6210<\/h2>\n\n\n\n<ul class=\"wp-block-list is-style-num_circle\">\n<li>\u8cfc\u5165\u8005\u304cGumroad\u3067\u8cfc\u5165\n<ul class=\"wp-block-list\">\n<li>  \u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u304c\u767a\u884c\u3055\u308c\u308b<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u30d7\u30e9\u30b0\u30a4\u30f3\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb \u2192 \u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u3092\u5165\u529b\n<ul class=\"wp-block-list\">\n<li>    WordPress REST API\u30b5\u30fc\u30d0\u30fc\u306b\u9001\u4fe1<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Gumroad API\u3067\u30e9\u30a4\u30bb\u30f3\u30b9\u691c\u8a3c \uff0b MySQL\u306b\u30b5\u30a4\u30c8\u767b\u9332\n<ul class=\"wp-block-list\">\n<li>    \u6700\u59273\u30b5\u30a4\u30c8\u307e\u3067\u7ba1\u7406<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u6709\u6599\u6a5f\u80fd\u304c\u4f7f\u3048\u308b\u3088\u3046\u306b\u306a\u308b<br><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>\u6280\u8853\u30b9\u30bf\u30c3\u30af<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list is-style-num_circle\">\n<li>\u8ca9\u58f2\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0: Gumroad<\/li>\n\n\n\n<li>\u30e9\u30a4\u30bb\u30f3\u30b9\u691c\u8a3c: Gumroad License Verify API\uff08\u7121\u6599\u30fbAPI\u30ad\u30fc\u4e0d\u8981\uff09<\/li>\n\n\n\n<li>\u30d0\u30c3\u30af\u30a8\u30f3\u30c9: WordPress REST API\uff08\u30b7\u30f3\u30ec\u30f3\u30bf\u30eb\u30b5\u30fc\u30d0\u30fc\uff09<\/li>\n\n\n\n<li>\u30c7\u30fc\u30bf\u4fdd\u5b58: MySQL\uff08WordPress\u306eDB\uff09<\/li>\n\n\n\n<li>\u30af\u30e9\u30a4\u30a2\u30f3\u30c8: \u8ca9\u58f2\u30d7\u30e9\u30b0\u30a4\u30f3\u5074PHP<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u305d\u308c\u3067\u306f\u3084\u308a\u65b9\u3092\u8a73\u3057\u304f\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u5b9f\u969b\u306e\u8a18\u9332<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u969b\u306e\u5c0e\u5165\u306e\u8a18\u9332\u3067\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Gumroad\u306b\u5546\u54c1\u3092\u767b\u9332<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u5546\u54c1\u767b\u9332\u6642\u306bLicense Key\u3092\u8ffd\u52a0\u3057\u3066\u3001\u81ea\u5206\u306eAPI\u30ad\u30fc\uff08xxxxxxxxxxxxxxxxxxx-X==\uff09\u3092\u30e1\u30e2\u3057\u3066\u304a\u304d\u307e\u3057\u3087\u3046\u3002<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"812\" height=\"500\" src=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h14_18-812x500.png\" alt=\"Gumroad\u3067\u306e\u5546\u54c1\u767b\u9332\u6642\u306b\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u306e\u9805\u76ee\u3092\u8ffd\u52a0\u3057\u3001API\u30ad\u30fc\u3092\u78ba\u8a8d\u3059\u308b\u753b\u9762\u306e\u69d8\u5b50\u3002\" class=\"wp-image-27853\" srcset=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h14_18-812x500.png 812w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h14_18-382x235.png 382w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h14_18-768x473.png 768w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h14_18.png 924w\" sizes=\"(max-width: 812px) 100vw, 812px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"812\" height=\"385\" src=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h48_31-812x385.png\" alt=\"\u81ea\u4f5c\u30d7\u30e9\u30b0\u30a4\u30f3\u306bGumroad\u30e9\u30a4\u30bb\u30f3\u30b9\u8a8d\u8a3c\u3092\u5b9f\u88c5\u3059\u308b\u624b\u9806\u306e\u30a4\u30e1\u30fc\u30b8\u3002\" class=\"wp-image-27856\" srcset=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h48_31-812x385.png 812w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h48_31-382x181.png 382w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h48_31-768x364.png 768w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h48_31.png 924w\" sizes=\"(max-width: 812px) 100vw, 812px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u306e\u74b0\u5883\u69cb\u7bc9<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u30b5\u30d6\u30c9\u30e1\u30a4\u30f3\u7b49\u3067\u30e9\u30a4\u30bb\u30f3\u30b9\u8a8d\u8a3c\u7528\u306bWordPress\u30b5\u30a4\u30c8\u3092\u7acb\u3061\u4e0a\u3052\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u3042\u3068\u306f\u4e0b\u8a18\u306e\u30b3\u30fc\u30c9\uff08my-license-server.php\uff09\u3092zip\u306b\u5727\u7e2e\u3057\u3066\u3001\u30d7\u30e9\u30b0\u30a4\u30f3\u3068\u3057\u3066\u6709\u52b9\u5316\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">WordPress\u306e\u7ba1\u7406\u753b\u9762\uff1e\u8a2d\u5b9a\uff1eLicense Server\u304b\u3089Gumroad Product ID\uff08xxxxxxxxxxxxxxxxxxx-X==\uff09\u3092\u767b\u9332\u3057\u3066\u4fdd\u5b58\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"812\" height=\"420\" src=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h56_10-812x420.png\" alt=\"WordPress\u306e\u8a2d\u5b9a\u753b\u9762\u306b\u3042\u308b\u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u9805\u76ee\u3067\u3001Gumroad\u306e\u30d7\u30ed\u30c0\u30af\u30c8ID\u3092\u767b\u9332\u3057\u3066\u4fdd\u5b58\u3059\u308b\u69d8\u5b50\u3002\" class=\"wp-image-27857\" srcset=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h56_10-812x420.png 812w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h56_10-382x198.png 382w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h56_10-768x398.png 768w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h56_10.png 924w\" sizes=\"(max-width: 812px) 100vw, 812px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u81ea\u4f5c\u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u8cfc\u5165<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Gumroad\u3067\u81ea\u4f5c\u30d7\u30e9\u30b0\u30a4\u30f3\u3092\u516c\u958b\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">0\u5186\u306b\u3057\u3066\u3001\u81ea\u5206\u306e\u30d7\u30e9\u30b0\u30a4\u30f3\u3092Gumroad\u304b\u3089\u8cfc\u5165\u3057\u307e\u3057\u3087\u3046\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u305d\u3057\u3066<strong>\u81ea\u5206\u3060\u3051\u306b\u767a\u884c\u3055\u308c\u308b\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc<\/strong>\u3092\u30b3\u30d4\u30fc\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"812\" height=\"315\" src=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h57_47-812x315.png\" alt=\"Gumroad\u3067\u81ea\u4f5c\u30d7\u30e9\u30b0\u30a4\u30f3\u3092\u8cfc\u5165\u3057\u3001\u767a\u884c\u3055\u308c\u305f\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u3092\u78ba\u8a8d\u30fb\u53d6\u5f97\u3059\u308b\u753b\u9762\u306e\u69d8\u5b50\u3002\" class=\"wp-image-27858\" srcset=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h57_47-812x315.png 812w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h57_47-382x148.png 382w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h57_47-768x298.png 768w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h57_47.png 924w\" sizes=\"(max-width: 812px) 100vw, 812px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u8cfc\u5165\u3057\u305f\u81ea\u4f5c\u30d7\u30e9\u30b0\u30a4\u30f3\u3092\u6709\u52b9\u5316\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u81ea\u4f5c\u30d7\u30e9\u30b0\u30a4\u30f3\u8a2d\u5b9a\u304b\u3089\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u3092\u5165\u308c\u3066\u4fdd\u5b58\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u6700\u5f8c\u306b\u3053\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u3092\u6709\u52b9\u5316\u3057\u307e\u3057\u3087\u3046\u3002<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-style-border\"><img decoding=\"async\" width=\"812\" height=\"550\" src=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h59_25-812x550.png\" alt=\"\u81ea\u4f5c\u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u8a2d\u5b9a\u753b\u9762\u3067\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u3092\u5165\u529b\u3057\u3001\u30e9\u30a4\u30bb\u30f3\u30b9\u3092\u6709\u52b9\u5316\u3059\u308b\u624b\u9806\u3092\u793a\u3059\u753b\u9762\u3002\" class=\"wp-image-27859\" srcset=\"https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h59_25-812x550.png 812w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h59_25-382x259.png 382w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h59_25-768x521.png 768w, https:\/\/motoki-design.co.jp\/wordpress\/wp-content\/uploads\/2026-04-11_14h59_25.png 924w\" sizes=\"(max-width: 812px) 100vw, 812px\" \/><\/figure>\n\n\n\n<p class=\"is-style-icon_info wp-block-paragraph\">\u4eca\u56de\u306e\u30d7\u30e9\u30b0\u30a4\u30f3\u306f3\u30c9\u30e1\u30a4\u30f3\u307e\u3067\u4f7f\u3048\u308b\u3068\u3044\u3046\u8a2d\u5b9a\u306b\u3057\u307e\u3057\u305f\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3084\u8a8d\u8a3c\u3067\u306e\u30cf\u30de\u308a\u3069\u3053\u308d\u3082\u66f8\u3044\u3066\u3044\u307e\u3059\u306e\u3067\u3001\u77e5\u898b\u304c\u3042\u308b\u3046\u3048\u3067AI\u7b49\u3092\u4f7f\u3063\u3066\u5b9f\u88c5\u3057\u305f\u307b\u3046\u304c\u65e9\u3044\u3067\u3059\u3002<\/p>\n\n\n\n<p class=\"is-style-icon_pen wp-block-paragraph\">\u5b9f\u969b\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u3084\u81ea\u4f5c\u30d7\u30e9\u30b0\u30a4\u30f3\u3067\u4f7f\u3063\u305f\u30b3\u30fc\u30c9\u3082\u5171\u6709\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u3010\u6700\u91cd\u8981\u3011\u4e8b\u524d\u306b\u77e5\u3063\u3066\u304a\u304f\u3079\u304dGumroad\u306e\u7f60<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u88c5\u3092\u59cb\u3081\u308b\u524d\u306b\u3001\u5fc5\u305a\u3053\u308c\u3092\u628a\u63e1\u3057\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u3053\u3092\u8aa4\u89e3\u3059\u308b\u3068\u4e38\u4e00\u65e5\u30cf\u30de\u308a\u307e\u3059\uff08\u5b9f\u969b\u306b\u30cf\u30de\u308a\u307e\u3057\u305f\uff09\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Gumroad\u306eProduct ID\u306f\u300c2\u7a2e\u985e\u300d\u3042\u308b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Gumroad\u306e\u7ba1\u7406\u753b\u9762\u3092\u898b\u308b\u3068\u3001\u4e00\u898b\u4f3c\u305f\u3088\u3046\u306a\u5024\u304c2\u3064\u5b58\u5728\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u540d\u79f0<\/th><th>\u5024\u306e\u4f8b<\/th><th>\u7528\u9014<\/th><\/tr><\/thead><tbody><tr><td><strong>Permalink<\/strong>\uff08URL\u30b9\u30e9\u30c3\u30b0\uff09<\/td><td><code>hogehoge<\/code><\/td><td>\u5546\u54c1URL\u7528\u3002verify API\u3067\u306f<strong>\u4f7f\u3048\u306a\u3044<\/strong><\/td><\/tr><tr><td><strong>Product ID<\/strong>\uff08API\u7528\uff09<\/td><td><code>xxxxxxxxxxxxxxxxxxx-X==<\/code><\/td><td>verify API\u3067\u4f7f\u3046\u3002<code>==<\/code>\u307e\u3067\u542b\u3080<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Product ID\u306e\u5834\u6240<\/strong>: Gumroad\u7ba1\u7406\u753b\u9762 \u2192 \u5546\u54c1\u3092\u958b\u304f \u2192 \u53f3\u5074\u306b\u300cUse your product ID to verify licenses through the API.\u300d\u3068\u66f8\u304b\u308c\u305f\u6b04\u306e\u4e0b\u306b\u8868\u793a\u3055\u308c\u3066\u3044\u308b\u6587\u5b57\u5217\u3067\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u300c<code>hogehoge<\/code>\u300d\u306e\u3088\u3046\u306a\u30b7\u30f3\u30d7\u30eb\u306aURL\u30b9\u30e9\u30c3\u30b0\u3092Product ID\u3068\u3057\u3066Gumroad API\u306b\u9001\u308b\u3068\u3001<code>That license does not exist for the provided product.<\/code> \u3068\u3044\u3046\u30a8\u30e9\u30fc\u304c\u8fd4\u3063\u3066\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>xxxxxxxxxxxxxxxxxxx-X==<\/code> \u306e\u3088\u3046\u306a<code>==<\/code>\u3067\u7d42\u308f\u308bBase64\u5f62\u5f0f\u306e\u6587\u5b57\u5217\u304c\u6b63\u3057\u3044Product ID\u3067\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Gumroad License Verify API\u306fAPI\u30ad\u30fc\u4e0d\u8981<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u4ee5\u4e0b\u306e\u3088\u3046\u306b\u3001product_id\u3068license_key\u3060\u3051\u3067\u691c\u8a3c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-bash\" data-lang=\"Bash\"><code>curl -X POST https:\/\/api.gumroad.com\/v2\/licenses\/verify \\\n  -d &quot;product_id=xxxxxxxxxxxxxxxxxxx-X==&quot; \\\n  -d &quot;license_key=XXXX-XXXX-XXXX-XXXX&quot; \\\n  -d &quot;increment_uses_count=false&quot;<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><code>increment_uses_count=false<\/code> \u3092\u6307\u5b9a\u3059\u308b\u3053\u3068\u3067Gumroad\u5074\u306e\u4f7f\u7528\u30ab\u30a6\u30f3\u30c8\u3092\u5897\u3084\u3055\u305a\u3001\u30b5\u30a4\u30c8\u6570\u7ba1\u7406\u306f\u81ea\u524d\u3067\u884c\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: \u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u7528WordPress\u3092\u8a2d\u7f6e<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u30b5\u30d6\u30c9\u30e1\u30a4\u30f3\u3092\u8ffd\u52a0\u3057\u3066WordPress\u3092\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u30ec\u30f3\u30bf\u30eb\u30b5\u30fc\u30d0\u30fc\u306e\u5834\u5408\u3001\u7ba1\u7406\u30d1\u30cd\u30eb\u304b\u3089 <code>api.example.com<\/code> \u306e\u3088\u3046\u306a\u30b5\u30d6\u30c9\u30e1\u30a4\u30f3\u3092\u8ffd\u52a0\u3057\u3001WordPress\u7c21\u5358\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3067WordPress\u3092\u5165\u308c\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u5f8c\u306b\u5fc5\u305a\u884c\u3046\u3053\u3068\uff1a<strong>\u8a2d\u5b9a \u2192 \u30d1\u30fc\u30de\u30ea\u30f3\u30af \u2192 \u300c\u6295\u7a3f\u540d\u300d\u306b\u5909\u66f4\u3057\u3066\u4fdd\u5b58<\/strong>\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u308c\u3092\u3057\u306a\u3044\u3068REST API\u306e\u30eb\u30fc\u30c8\u304c\u8a8d\u8b58\u3055\u308c\u307e\u305b\u3093\uff08<code>rest_no_route<\/code> \u30a8\u30e9\u30fc\u306e\u539f\u56e0\u306b\u306a\u308a\u307e\u3059\uff09\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u30d7\u30e9\u30b0\u30a4\u30f3\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u4ee5\u4e0b\u306e\u69cb\u6210\u3067\u30d7\u30e9\u30b0\u30a4\u30f3\u30d5\u30a1\u30a4\u30eb\u3092\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>\/wp-content\/plugins\/My-license-server\/\n    \u2514\u2500\u2500 My-license-server.php<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">WordPress\u7ba1\u7406\u753b\u9762 \u2192 \u30d7\u30e9\u30b0\u30a4\u30f3 \u2192 \u300cMY License Server\u300d\u3092\u6709\u52b9\u5316\u3059\u308b\u3068\u3001\u4ee5\u4e0b\u306e3\u30c6\u30fc\u30d6\u30eb\u304c\u81ea\u52d5\u4f5c\u6210\u3055\u308c\u307e\u3059\u3002<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>\u30c6\u30fc\u30d6\u30eb\u540d<\/th><th>\u7528\u9014<\/th><\/tr><\/thead><tbody><tr><td><code>wp_mls_licenses<\/code><\/td><td>\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u7ba1\u7406\uff08\u30cf\u30c3\u30b7\u30e5\u5316\u3057\u3066\u4fdd\u5b58\uff09<\/td><\/tr><tr><td><code>wp_mls_activations<\/code><\/td><td>\u6709\u52b9\u5316\u30b5\u30a4\u30c8\u7ba1\u7406<\/td><\/tr><tr><td><code>wp_mls_logs<\/code><\/td><td>\u30c7\u30d0\u30c3\u30b0\u7528\u30ed\u30b0<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u306e\u5e73\u6587\u306fDB\u306b\u4fdd\u5b58\u3057\u307e\u305b\u3093\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">SHA-256\u30cf\u30c3\u30b7\u30e5\u306e\u307f\u4fdd\u5b58\u3057\u3001\u30ed\u30b0\u306b\u306f\u672b\u5c3e4\u6587\u5b57\u3060\u3051\u6b8b\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Gumroad Product ID\u3092DB\u306b\u767b\u9332<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">WordPress\u7ba1\u7406\u753b\u9762 \u2192 <strong>\u8a2d\u5b9a \u2192 License Server<\/strong> \u3092\u958b\u304d\u3001Gumroad Product ID\u306b <code>xxxxxxxxxxxxxxxxxxx-X==<\/code>\uff08<code>==<\/code>\u307e\u3067\u542b\u3080\uff09\u3092\u5165\u529b\u3057\u3066\u4fdd\u5b58\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u26a0\ufe0f \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d7\u30e9\u30b0\u30a4\u30f3\u3067\u4fdd\u5b58\u3067\u304d\u306a\u3044\u5834\u5408<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">XO Security\u306a\u3069\u306e\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d7\u30e9\u30b0\u30a4\u30f3\u304cWordPress\u306e <code>options.php<\/code> \u3078\u306ePOST\u30ea\u30af\u30a8\u30b9\u30c8\u306b\u5e72\u6e09\u3059\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u305d\u306e\u5834\u5408\u306f\u7ba1\u7406\u753b\u9762\u3067\u306e\u4fdd\u5b58\u304c\u52b9\u304b\u306a\u3044\u305f\u3081\u3001phpMyAdmin\u3067\u76f4\u63a5\u767b\u9332\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">phpMyAdmin \u2192 \u5bfe\u8c61DB \u2192 SQL\u30bf\u30d6\u3067\u4ee5\u4e0b\u3092\u5b9f\u884c\uff1a<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-sql\" data-lang=\"SQL\"><code>INSERT INTO wp_options (option_name, option_value, autoload)\nVALUES (&#39;mls_product_id&#39;, &#39;xxxxxxxxxxxxxxxxxxx-X==&#39;, &#39;yes&#39;)\nON DUPLICATE KEY UPDATE option_value = &#39;xxxxxxxxxxxxxxxxxxx-X==&#39;;<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">\u767b\u9332\u78ba\u8a8d\uff1a<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>SELECT option_name, option_value FROM wp_options\nWHERE option_name = &#39;mls_product_id&#39;;<\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: \u52d5\u4f5c\u78ba\u8a8d\uff08curl\uff09<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Windows\u306e\u5834\u5408\u306f1\u884c\u3067\u5b9f\u884c<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Windows\u306e\u30b3\u30de\u30f3\u30c9\u30d7\u30ed\u30f3\u30d7\u30c8\u306f\u30d0\u30c3\u30af\u30b9\u30e9\u30c3\u30b7\u30e5 <code>\\<\/code> \u306b\u3088\u308b\u6539\u884c\u304c\u3067\u304d\u307e\u305b\u3093\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u4ee5\u4e0b\u306e\u3088\u3046\u306b<strong>1\u884c\u3067<\/strong>\u8cbc\u308a\u4ed8\u3051\u3066\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>curl -X POST https:\/\/api.example.com\/wp-json\/mls\/v1\/license -H &quot;Content-Type: application\/json&quot; -d &quot;{\\&quot;action\\&quot;:\\&quot;activate\\&quot;,\\&quot;license_key\\&quot;:\\&quot;XXXX-XXXX-XXXX-XXXX\\&quot;,\\&quot;site_url\\&quot;:\\&quot;https:\/\/test.example.com\\&quot;,\\&quot;product_id\\&quot;:\\&quot;xxxxxxxxxxxxxxxxxxx-X==\\&quot;,\\&quot;plugin_version\\&quot;:\\&quot;1.0.0\\&quot;}&quot;<\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">\u671f\u5f85\u3059\u308b\u30ec\u30b9\u30dd\u30f3\u30b9<\/h3>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>{\n  &quot;ok&quot;: true,\n  &quot;status&quot;: &quot;active&quot;,\n  &quot;message&quot;: &quot;activated&quot;,\n  &quot;site_count&quot;: 1,\n  &quot;max_sites&quot;: 3,\n  &quot;domain&quot;: &quot;test.example.com&quot;\n}<\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Mac \/ Linux\u306e\u5834\u5408<\/h3>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>curl -X POST https:\/\/api.example.com\/wp-json\/mls\/v1\/license \\\n  -H &quot;Content-Type: application\/json&quot; \\\n  -d &#39;{\n    &quot;action&quot;: &quot;activate&quot;,\n    &quot;license_key&quot;: &quot;XXXX-XXXX-XXXX-XXXX&quot;,\n    &quot;site_url&quot;: &quot;https:\/\/test.example.com&quot;,\n    &quot;product_id&quot;: &quot;xxxxxxxxxxxxxxxxxxx-X==&quot;,\n    &quot;plugin_version&quot;: &quot;1.0.0&quot;\n  }&#39;\n<\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: \u8ca9\u58f2\u30d7\u30e9\u30b0\u30a4\u30f3\u3078\u306e\u7d44\u307f\u8fbc\u307f<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u30e9\u30a4\u30bb\u30f3\u30b9API\u306eURL\u3092\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u3068\u3057\u3066\u57cb\u3081\u8fbc\u3080<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u8cfc\u5165\u8005\u304cURL\u3092\u624b\u5165\u529b\u3057\u306a\u304f\u3066\u6e08\u3080\u3088\u3046\u3001\u8a2d\u5b9a\u753b\u9762\u306e\u30e9\u30a4\u30bb\u30f3\u30b9API URL\u6b04\u306b\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u3092\u8a2d\u5b9a\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>\/\/ \u5909\u66f4\u524d\nvalue=&quot;&lt;?php echo esc_attr((string) ($settings[&#39;license_api_url&#39;] ?? &#39;&#39;)); ?&gt;&quot;\n\n\/\/ \u5909\u66f4\u5f8c\uff08?: \u3067\u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af\uff09\nvalue=&quot;&lt;?php echo esc_attr((string) ($settings[&#39;license_api_url&#39;] \n    ?: &#39;https:\/\/api.example.com\/wp-json\/mls\/v1\/license&#39;)); ?&gt;&quot;<\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">\u6709\u6599\u6a5f\u80fd\u3092\u30e9\u30a4\u30bb\u30f3\u30b9\u3067\u30ac\u30fc\u30c9\u3059\u308b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u8a8d\u8a3c\u3057\u3066\u3044\u306a\u3044\u5834\u5408\u306b\u6a5f\u80fd\u3092\u6b62\u3081\u308b\u306b\u306f\u3001AJAX\u30cf\u30f3\u30c9\u30e9\u306e\u5171\u901a\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u95a2\u6570\u306b\u30c1\u30a7\u30c3\u30af\u3092\u8ffd\u52a0\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>function amu_ai_validate_rewrite_request(): void\n{\n    check_ajax_referer(&#39;amu_ai_rewriter&#39;, &#39;nonce&#39;);\n\n    \/\/ \u30e9\u30a4\u30bb\u30f3\u30b9\u30c1\u30a7\u30c3\u30af\u3092\u8ffd\u52a0\n    if ( ! function_exists(&#39;amu_ai_license_is_recently_valid&#39;) \n         || ! amu_ai_license_is_recently_valid() ) {\n        wp_send_json_error( [\n            &#39;message&#39; =&gt; &#39;\u30e9\u30a4\u30bb\u30f3\u30b9\u304c\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u30e9\u30a4\u30bb\u30f3\u30b9\u8a2d\u5b9a\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002&#39;\n        ], 403 );\n    }\n\n    \/\/ \u4ee5\u4e0b\u306f\u65e2\u5b58\u306e\u30b3\u30fc\u30c9\n    $post_id = isset($_POST[&#39;postId&#39;]) ? (int) $_POST[&#39;postId&#39;] : 0;\n    if ($post_id &lt;= 0 || ! current_user_can(&#39;edit_post&#39;, $post_id)) {\n        wp_send_json_error([&#39;message&#39; =&gt; &#39;Permission denied.&#39;], 403);\n    }\n}\n<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">\u30ea\u30e9\u30a4\u30c8\u30fb\u8a18\u4e8b\u751f\u6210\u30fb\u6a5f\u4f1a\u30d7\u30e9\u30f3\u30ca\u30fc\u306a\u3069\u3001AI\u3092\u4f7f\u3046\u5168\u6a5f\u80fd\u306e\u5171\u901a\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u95a2\u6570\u306b\u3053\u308c\u3092\u8ffd\u52a0\u3059\u308c\u3070\u4e00\u62ec\u3067\u30ac\u30fc\u30c9\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">ZIP\u3092\u30d1\u30c3\u30b1\u30fc\u30b8\u3059\u308b\u969b\u306e\u6ce8\u610f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Windows\u3067\u4f5c\u6210\u3057\u305fZIP\u306f\u30d1\u30b9\u533a\u5207\u308a\u6587\u5b57\u304c\u30d0\u30c3\u30af\u30b9\u30e9\u30c3\u30b7\u30e5\uff08<code>\\<\/code>\uff09\u306b\u306a\u308a\u3001\u30b7\u30f3\u30ec\u30f3\u30bf\u30eb\u30b5\u30fc\u30d0\u30fc\uff08Linux\uff09\u3067\u5c55\u958b\u3059\u308b\u3068\u30d5\u30a9\u30eb\u30c0\u69cb\u9020\u304c\u58ca\u308c\u3066\u30d7\u30e9\u30b0\u30a4\u30f3\u304c\u52d5\u304d\u307e\u305b\u3093\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Python\u3067\u6b63\u3057\u304f\u518d\u30d1\u30c3\u30b1\u30fc\u30b8\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>import zipfile\n\nwith zipfile.ZipFile(&#39;original.zip&#39;) as zin, \\\n     zipfile.ZipFile(&#39;fixed.zip&#39;, &#39;w&#39;, zipfile.ZIP_DEFLATED) as zout:\n    for item in zin.infolist():\n        new_name = item.filename.replace(&#39;\\\\&#39;, &#39;\/&#39;)  # \u30d0\u30c3\u30af\u30b9\u30e9\u30c3\u30b7\u30e5\u3092\u4fee\u6b63\n        zout.writestr(new_name, zin.read(item.filename))<\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">\u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u306e\u4ed5\u7d44\u307f<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">API\u4ed5\u69d8<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">3\u3064\u306e\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3092\u3059\u3079\u3066\u540c\u4e00URL\uff08<code>POST \/wp-json\/mls\/v1\/license<\/code>\uff09\u306b\u96c6\u7d04\u3057\u3001<code>action<\/code> \u30d1\u30e9\u30e1\u30fc\u30bf\u3067\u5206\u5c90\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>action<\/th><th>\u51e6\u7406<\/th><\/tr><\/thead><tbody><tr><td><code>activate<\/code><\/td><td>\u30e9\u30a4\u30bb\u30f3\u30b9\u691c\u8a3c \u2192 \u30b5\u30a4\u30c8\u767b\u9332\uff08\u6700\u59273\u30b5\u30a4\u30c8\uff09<\/td><\/tr><tr><td><code>validate<\/code><\/td><td>\u5b9a\u671f\u7684\u306a\u6709\u52b9\u6027\u78ba\u8a8d\uff0824\u6642\u9593\u3054\u3068\uff09<\/td><\/tr><tr><td><code>deactivate<\/code><\/td><td>\u30b5\u30a4\u30c8\u306e\u89e3\u9664\uff08\u5225\u30b5\u30a4\u30c8\u3078\u79fb\u884c\u53ef\u80fd\uff09<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u30e9\u30a4\u30bb\u30f3\u30b9\u8a8d\u8a3c\u306e\u6d41\u308c<\/h3>\n\n\n\n<ol class=\"wp-block-list is-style-num_circle\">\n<li>\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\uff0b\u30b5\u30a4\u30c8URL\u3092\u9001\u4fe1<\/li>\n\n\n\n<li>\u30b5\u30fc\u30d0\u30fc\u304cGumroad API\u3067\u30e9\u30a4\u30bb\u30f3\u30b9\u691c\u8a3c<\/li>\n\n\n\n<li>\u8fd4\u91d1\u30fb\u7d1b\u4e89\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u306f\u81ea\u52d5\u3067\u7121\u52b9\u5316<\/li>\n\n\n\n<li>\u6709\u52b9\u306a\u3089 <code>wp_mls_activations<\/code> \u306b\u30b5\u30a4\u30c8\u3092\u767b\u9332<\/li>\n\n\n\n<li>3\u30b5\u30a4\u30c8\u8d85\u3048\u305f\u3089 <code>limit_exceeded<\/code> \u3092\u8fd4\u3059<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">\u30ad\u30e3\u30c3\u30b7\u30e5\u3068\u7336\u4e88\u671f\u9593<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u6bce\u56de\u30b5\u30fc\u30d0\u30fc\u306b\u554f\u3044\u5408\u308f\u305b\u308b\u3068\u8ca0\u8377\u304c\u304b\u304b\u308b\u305f\u3081\u3001\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u5074\u3067\u30ad\u30e3\u30c3\u30b7\u30e5\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>24\u6642\u9593\u4ee5\u5185<\/strong>: \u524d\u56de\u306e\u7d50\u679c\u3092\u305d\u306e\u307e\u307e\u4f7f\u7528\uff08\u30b5\u30fc\u30d0\u30fc\u3078\u306e\u901a\u4fe1\u306a\u3057\uff09<\/li>\n\n\n\n<li><strong>24\u6642\u9593\u7d4c\u904e<\/strong>: \u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u3067\u518d\u691c\u8a3c<\/li>\n\n\n\n<li><strong>\u901a\u4fe1\u5931\u6557\u6642<\/strong>: \u524d\u56de\u6210\u529f\u304b\u3089<strong>7\u65e5\u9593<\/strong>\u306f\u6a5f\u80fd\u3092\u7dad\u6301\u3059\u308b\u7336\u4e88\u671f\u9593\u3042\u308a<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">\u30e9\u30a4\u30bb\u30f3\u30b9\u7ba1\u7406\uff08\u904b\u55b6\u8005\u5411\u3051\uff09<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e0d\u6b63\u5229\u7528\u8005\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u3092\u5931\u52b9\u3055\u305b\u308b<\/h3>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>UPDATE wp_mls_licenses\nSET status = &#39;revoked&#39;\nWHERE license_key_last4 = &#39;1100&#39;;  -- \u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u672b\u5c3e4\u6587\u5b57\u3067\u7279\u5b9a<\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">\u7279\u5b9a\u30b5\u30a4\u30c8\u3092\u5f37\u5236\u89e3\u9664\u3059\u308b<\/h3>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-plain\"><code>UPDATE wp_mls_activations\nSET status = &#39;inactive&#39;, deactivated_at = NOW()\nWHERE domain = &#39;problem-site.com&#39;;<\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">\u8fd4\u91d1\u5bfe\u5fdc\u306b\u3064\u3044\u3066<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Gumroad API\u3067\u30e9\u30a4\u30bb\u30f3\u30b9\u691c\u8a3c\u3059\u308b\u969b\u3001\u30ec\u30b9\u30dd\u30f3\u30b9\u306b <code>refunded: true<\/code> \u304c\u542b\u307e\u308c\u3066\u3044\u308c\u3070\u81ea\u52d5\u3067\u7121\u52b9\u5316\u3055\u308c\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">API\u30ad\u30fc\u4e0d\u8981\u3067\u52d5\u4f5c\u3059\u308b\u305f\u3081\u3001\u8fd4\u91d1\u6642\u306e\u624b\u52d5\u5bfe\u5fdc\u306f\u4e0d\u8981\u3067\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u306a\u304a\u3001\u30c7\u30b8\u30bf\u30eb\u30b3\u30f3\u30c6\u30f3\u30c4\u306f\u898f\u7d04\u306b\u300c\u8fd4\u91d1\u4e0d\u53ef\u300d\u3068\u660e\u8a18\u3057\u3066\u304a\u304f\u3053\u3068\u3092\u63a8\u5968\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Gumroad\u306e\u5546\u54c1\u8a2d\u5b9a\u3067\u3082\u300cNo refunds\u300d\u3092\u9078\u629e\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u30c8\u30e9\u30d6\u30eb\u30b7\u30e5\u30fc\u30c6\u30a3\u30f3\u30b0<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>\u30a8\u30e9\u30fc<\/th><th>\u539f\u56e0<\/th><th>\u5bfe\u51e6<\/th><\/tr><\/thead><tbody><tr><td><code>rest_no_route<\/code><\/td><td>\u30d1\u30fc\u30de\u30ea\u30f3\u30af\u8a2d\u5b9a\u304c\u300c\u57fa\u672c\u300d<\/td><td>\u8a2d\u5b9a\u2192\u30d1\u30fc\u30de\u30ea\u30f3\u30af\u2192\u300c\u6295\u7a3f\u540d\u300d\u3067\u518d\u4fdd\u5b58<\/td><\/tr><tr><td><code>That license does not exist for the provided product.<\/code><\/td><td>URL\u30b9\u30e9\u30c3\u30b0\uff08<code>hogehoge<\/code>\uff09\u3092Product ID\u3068\u3057\u3066\u4f7f\u3063\u3066\u3044\u308b<\/td><td><code>==<\/code>\u3067\u7d42\u308f\u308bBase64\u5f62\u5f0f\u306eProduct ID\u3092\u4f7f\u3046<\/td><\/tr><tr><td><code>License API response was invalid.<\/code><\/td><td>\u30ec\u30b9\u30dd\u30f3\u30b9\u304cJSON\u3067\u306a\u3044<\/td><td><code>curl -v<\/code> \u3067HTTP\u30b9\u30c6\u30fc\u30bf\u30b9\u3092\u78ba\u8a8d<\/td><\/tr><tr><td>\u7ba1\u7406\u753b\u9762\u306e\u8a2d\u5b9a\u304c\u4fdd\u5b58\u3055\u308c\u306a\u3044<\/td><td>XO Security\u7b49\u304c\u5e72\u6e09<\/td><td>phpMyAdmin\u3067\u76f4\u63a5INSERT<\/td><\/tr><tr><td>ZIP\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u5f8c\u306b\u30a8\u30e9\u30fc<\/td><td>Windows\u30d0\u30c3\u30af\u30b9\u30e9\u30c3\u30b7\u30e5\u554f\u984c<\/td><td>Python\u3067ZIP\u3092\u518d\u30d1\u30c3\u30b1\u30fc\u30b8<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">\u5b9f\u969b\u306e\u30b3\u30fc\u30c9<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u969b\u306e\u30b3\u30fc\u30c9\u3067\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u53c2\u8003\u306b\u3057\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u7528\u30d7\u30e9\u30b0\u30a4\u30f3<\/h3>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>&lt;?php\n\/**\n * Plugin Name: Motoki License Server\n * Description: Gumroad\u9023\u643aWordPress\u6709\u6599\u30d7\u30e9\u30b0\u30a4\u30f3\u7528\u30e9\u30a4\u30bb\u30f3\u30b9\u7ba1\u7406\u30b5\u30fc\u30d0\u30fc\n * Version: 1.0.0\n * Author: MOTOKI\u5408\u540c\u4f1a\u793e\n *\/\n\ndefined( &#39;ABSPATH&#39; ) || exit;\n\ndefine( &#39;MLS_VERSION&#39;, &#39;1.0.0&#39; );\ndefine( &#39;MLS_PLUGIN_DIR&#39;, plugin_dir_path( __FILE__ ) );\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u6709\u52b9\u5316\u6642: \u30c6\u30fc\u30d6\u30eb\u4f5c\u6210\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nregister_activation_hook( __FILE__, &#39;mls_activate&#39; );\nfunction mls_activate() {\n    mls_create_tables();\n}\n\nfunction mls_create_tables() {\n    global $wpdb;\n    $charset = $wpdb-&gt;get_charset_collate();\n\n    \/\/ licenses \u30c6\u30fc\u30d6\u30eb\n    $sql_licenses = &quot;CREATE TABLE IF NOT EXISTS {$wpdb-&gt;prefix}mls_licenses (\n        id              BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,\n        license_key_hash VARCHAR(64)  NOT NULL,\n        license_key_last4 CHAR(4)     NOT NULL,\n        product_id      VARCHAR(100)  NOT NULL,\n        status          ENUM(&#39;active&#39;,&#39;revoked&#39;,&#39;invalid&#39;) NOT NULL DEFAULT &#39;active&#39;,\n        max_sites       TINYINT UNSIGNED NOT NULL DEFAULT 3,\n        gumroad_email   VARCHAR(255)  NOT NULL DEFAULT &#39;&#39;,\n        gumroad_purchase_id VARCHAR(100) NOT NULL DEFAULT &#39;&#39;,\n        created_at      DATETIME      NOT NULL,\n        updated_at      DATETIME      NOT NULL,\n        last_verified_at DATETIME     NULL,\n        PRIMARY KEY (id),\n        UNIQUE KEY uq_hash_product (license_key_hash, product_id),\n        KEY idx_hash (license_key_hash)\n    ) $charset;&quot;;\n\n    \/\/ activations \u30c6\u30fc\u30d6\u30eb\n    $sql_activations = &quot;CREATE TABLE IF NOT EXISTS {$wpdb-&gt;prefix}mls_activations (\n        id              BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,\n        license_key_hash VARCHAR(64)  NOT NULL,\n        site_hash       VARCHAR(64)   NOT NULL,\n        domain          VARCHAR(255)  NOT NULL,\n        normalized_site VARCHAR(255)  NOT NULL,\n        status          ENUM(&#39;active&#39;,&#39;inactive&#39;) NOT NULL DEFAULT &#39;active&#39;,\n        activated_at    DATETIME      NOT NULL,\n        deactivated_at  DATETIME      NULL,\n        last_seen_at    DATETIME      NOT NULL,\n        PRIMARY KEY (id),\n        UNIQUE KEY uq_license_site (license_key_hash, site_hash),\n        KEY idx_license (license_key_hash)\n    ) $charset;&quot;;\n\n    \/\/ logs \u30c6\u30fc\u30d6\u30eb\n    $sql_logs = &quot;CREATE TABLE IF NOT EXISTS {$wpdb-&gt;prefix}mls_logs (\n        id              BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,\n        timestamp       DATETIME      NOT NULL,\n        action          VARCHAR(20)   NOT NULL,\n        license_key_last4 CHAR(4)    NOT NULL DEFAULT &#39;&#39;,\n        domain          VARCHAR(255)  NOT NULL DEFAULT &#39;&#39;,\n        result          VARCHAR(20)   NOT NULL,\n        message         TEXT          NOT NULL,\n        PRIMARY KEY (id),\n        KEY idx_timestamp (timestamp)\n    ) $charset;&quot;;\n\n    require_once ABSPATH . &#39;wp-admin\/includes\/upgrade.php&#39;;\n    dbDelta( $sql_licenses );\n    dbDelta( $sql_activations );\n    dbDelta( $sql_logs );\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ REST API \u767b\u9332\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nadd_action( &#39;rest_api_init&#39;, &#39;mls_register_routes&#39; );\nfunction mls_register_routes() {\n    register_rest_route( &#39;mls\/v1&#39;, &#39;\/license&#39;, [\n        &#39;methods&#39;             =&gt; &#39;POST&#39;,\n        &#39;callback&#39;            =&gt; &#39;mls_handle_request&#39;,\n        &#39;permission_callback&#39; =&gt; &#39;__return_true&#39;, \/\/ \u8a8d\u8a3c\u306f\u30a2\u30d7\u30ea\u5074\u3067\u884c\u3046\n    ] );\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u30e1\u30a4\u30f3\u30cf\u30f3\u30c9\u30e9\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction mls_handle_request( WP_REST_Request $request ) {\n    $body = $request-&gt;get_json_params();\n    if ( empty( $body ) ) {\n        \/\/ form-urlencoded \u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af\n        $body = $request-&gt;get_params();\n    }\n\n    $action = sanitize_text_field( $body[&#39;action&#39;] ?? &#39;&#39; );\n\n    switch ( $action ) {\n        case &#39;activate&#39;:\n            return mls_action_activate( $body );\n        case &#39;validate&#39;:\n            return mls_action_validate( $body );\n        case &#39;deactivate&#39;:\n            return mls_action_deactivate( $body );\n        default:\n            return mls_json_error( &#39;invalid_action&#39;, &#39;\u4e0d\u6b63\u306a\u30a2\u30af\u30b7\u30e7\u30f3\u3067\u3059&#39;, 400 );\n    }\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ activate\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction mls_action_activate( array $body ) {\n    \/\/ \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\n    $v = mls_validate_input( $body, [ &#39;license_key&#39;, &#39;site_url&#39; ] );\n    if ( is_wp_error( $v ) ) {\n        return mls_json_error( &#39;validation_error&#39;, $v-&gt;get_error_message(), 400 );\n    }\n\n    $license_key = $body[&#39;license_key&#39;];\n    $product_id  = mls_get_product_id( $body );\n    $site_url    = $body[&#39;site_url&#39;];\n\n    $normalized  = mls_normalize_site_url( $site_url );\n    $key_hash    = mls_sha256( $license_key );\n    $site_hash   = mls_sha256( $normalized );\n    $key_last4   = substr( $license_key, -4 );\n\n    \/\/ Gumroad verify\n    $gumroad = mls_verify_gumroad( $license_key, $product_id );\n    if ( ! $gumroad[&#39;valid&#39;] ) {\n        mls_write_log( &#39;activate&#39;, $key_last4, $normalized, &#39;fail&#39;, &#39;Gumroad invalid: &#39; . $gumroad[&#39;reason&#39;] );\n        return mls_json_error( &#39;license_invalid&#39;, &#39;\u30e9\u30a4\u30bb\u30f3\u30b9\u304c\u7121\u52b9\u3067\u3059&#39;, 403 );\n    }\n\n    global $wpdb;\n    $now = current_time( &#39;mysql&#39; );\n\n    \/\/ licenses upsert\n    $license = mls_find_license( $key_hash, $product_id );\n    if ( ! $license ) {\n        $wpdb-&gt;insert( $wpdb-&gt;prefix . &#39;mls_licenses&#39;, [\n            &#39;license_key_hash&#39;    =&gt; $key_hash,\n            &#39;license_key_last4&#39;   =&gt; $key_last4,\n            &#39;product_id&#39;          =&gt; $product_id,\n            &#39;status&#39;              =&gt; &#39;active&#39;,\n            &#39;max_sites&#39;           =&gt; 3,\n            &#39;gumroad_email&#39;       =&gt; $gumroad[&#39;email&#39;],\n            &#39;gumroad_purchase_id&#39; =&gt; $gumroad[&#39;purchase_id&#39;],\n            &#39;created_at&#39;          =&gt; $now,\n            &#39;updated_at&#39;          =&gt; $now,\n            &#39;last_verified_at&#39;    =&gt; $now,\n        ] );\n        $license = mls_find_license( $key_hash, $product_id );\n    } else {\n        \/\/ revoked \u30c1\u30a7\u30c3\u30af\n        if ( $license-&gt;status === &#39;revoked&#39; ) {\n            mls_write_log( &#39;activate&#39;, $key_last4, $normalized, &#39;fail&#39;, &#39;License revoked&#39; );\n            return mls_json_error( &#39;license_revoked&#39;, &#39;\u3053\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u306f\u7121\u52b9\u5316\u3055\u308c\u3066\u3044\u307e\u3059&#39;, 403 );\n        }\n        \/\/ last_verified_at \u66f4\u65b0\n        $wpdb-&gt;update(\n            $wpdb-&gt;prefix . &#39;mls_licenses&#39;,\n            [ &#39;last_verified_at&#39; =&gt; $now, &#39;updated_at&#39; =&gt; $now, &#39;gumroad_email&#39; =&gt; $gumroad[&#39;email&#39;] ],\n            [ &#39;id&#39; =&gt; $license-&gt;id ]\n        );\n    }\n\n    $max_sites = (int) $license-&gt;max_sites;\n\n    \/\/ \u540c\u4e00\u30b5\u30a4\u30c8\u304c\u65e2\u306b\u30a2\u30af\u30c6\u30a3\u30d6 \u2192 \u51aa\u7b49\u6210\u529f\n    $existing = mls_find_active_activation( $key_hash, $site_hash );\n    if ( $existing ) {\n        $wpdb-&gt;update(\n            $wpdb-&gt;prefix . &#39;mls_activations&#39;,\n            [ &#39;last_seen_at&#39; =&gt; $now ],\n            [ &#39;id&#39; =&gt; $existing-&gt;id ]\n        );\n        $count = mls_count_active_activations( $key_hash );\n        mls_write_log( &#39;activate&#39;, $key_last4, $normalized, &#39;ok&#39;, &#39;Already active (idempotent)&#39; );\n        return rest_ensure_response( [\n            &#39;ok&#39;        =&gt; true,\n            &#39;status&#39;    =&gt; &#39;active&#39;,\n            &#39;message&#39;   =&gt; &#39;already_active&#39;,\n            &#39;site_count&#39; =&gt; $count,\n            &#39;max_sites&#39; =&gt; $max_sites,\n            &#39;domain&#39;    =&gt; $normalized,\n        ] );\n    }\n\n    \/\/ \u4f7f\u7528\u6570\u30c1\u30a7\u30c3\u30af\n    $count = mls_count_active_activations( $key_hash );\n    if ( $count &gt;= $max_sites ) {\n        mls_write_log( &#39;activate&#39;, $key_last4, $normalized, &#39;fail&#39;, &quot;Limit exceeded: {$count}\/{$max_sites}&quot; );\n        return rest_ensure_response( [\n            &#39;ok&#39;        =&gt; false,\n            &#39;status&#39;    =&gt; &#39;limit_exceeded&#39;,\n            &#39;message&#39;   =&gt; &#39;\u6709\u52b9\u5316\u3067\u304d\u308b\u30b5\u30a4\u30c8\u6570\u306e\u4e0a\u9650\u306b\u9054\u3057\u3066\u3044\u307e\u3059&#39;,\n            &#39;site_count&#39; =&gt; $count,\n            &#39;max_sites&#39; =&gt; $max_sites,\n        ] );\n    }\n\n    \/\/ activation \u4f5c\u6210\uff08UPSERT: inactive\u884c\u304c\u3042\u308c\u3070\u518dactive\u5316\uff09\n    $row = $wpdb-&gt;get_row( $wpdb-&gt;prepare(\n        &quot;SELECT * FROM {$wpdb-&gt;prefix}mls_activations WHERE license_key_hash=%s AND site_hash=%s&quot;,\n        $key_hash, $site_hash\n    ) );\n\n    if ( $row ) {\n        $wpdb-&gt;update(\n            $wpdb-&gt;prefix . &#39;mls_activations&#39;,\n            [ &#39;status&#39; =&gt; &#39;active&#39;, &#39;activated_at&#39; =&gt; $now, &#39;deactivated_at&#39; =&gt; null, &#39;last_seen_at&#39; =&gt; $now, &#39;domain&#39; =&gt; $normalized ],\n            [ &#39;id&#39; =&gt; $row-&gt;id ]\n        );\n    } else {\n        $wpdb-&gt;insert( $wpdb-&gt;prefix . &#39;mls_activations&#39;, [\n            &#39;license_key_hash&#39; =&gt; $key_hash,\n            &#39;site_hash&#39;        =&gt; $site_hash,\n            &#39;domain&#39;           =&gt; $normalized,\n            &#39;normalized_site&#39;  =&gt; $normalized,\n            &#39;status&#39;           =&gt; &#39;active&#39;,\n            &#39;activated_at&#39;     =&gt; $now,\n            &#39;deactivated_at&#39;   =&gt; null,\n            &#39;last_seen_at&#39;     =&gt; $now,\n        ] );\n    }\n\n    $count = mls_count_active_activations( $key_hash );\n    mls_write_log( &#39;activate&#39;, $key_last4, $normalized, &#39;ok&#39;, &quot;Activated. {$count}\/{$max_sites}&quot; );\n\n    return rest_ensure_response( [\n        &#39;ok&#39;        =&gt; true,\n        &#39;status&#39;    =&gt; &#39;active&#39;,\n        &#39;message&#39;   =&gt; &#39;activated&#39;,\n        &#39;site_count&#39; =&gt; $count,\n        &#39;max_sites&#39; =&gt; $max_sites,\n        &#39;domain&#39;    =&gt; $normalized,\n    ] );\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ validate\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction mls_action_validate( array $body ) {\n    $v = mls_validate_input( $body, [ &#39;license_key&#39;, &#39;site_url&#39; ] );\n    if ( is_wp_error( $v ) ) {\n        return mls_json_error( &#39;validation_error&#39;, $v-&gt;get_error_message(), 400 );\n    }\n\n    $license_key = $body[&#39;license_key&#39;];\n    $product_id  = mls_get_product_id( $body );\n    $site_url    = $body[&#39;site_url&#39;];\n\n    $normalized = mls_normalize_site_url( $site_url );\n    $key_hash   = mls_sha256( $license_key );\n    $site_hash  = mls_sha256( $normalized );\n    $key_last4  = substr( $license_key, -4 );\n\n    \/\/ Gumroad verify\n    $gumroad = mls_verify_gumroad( $license_key, $product_id );\n    if ( ! $gumroad[&#39;valid&#39;] ) {\n        mls_write_log( &#39;validate&#39;, $key_last4, $normalized, &#39;fail&#39;, &#39;Gumroad invalid: &#39; . $gumroad[&#39;reason&#39;] );\n        return rest_ensure_response( [\n            &#39;ok&#39;     =&gt; true,\n            &#39;valid&#39;  =&gt; false,\n            &#39;status&#39; =&gt; &#39;license_invalid&#39;,\n        ] );\n    }\n\n    \/\/ \u30e9\u30a4\u30bb\u30f3\u30b9\u884c\u30c1\u30a7\u30c3\u30af\n    $license = mls_find_license( $key_hash, $product_id );\n    if ( ! $license || $license-&gt;status === &#39;revoked&#39; ) {\n        mls_write_log( &#39;validate&#39;, $key_last4, $normalized, &#39;fail&#39;, &#39;License not found or revoked&#39; );\n        return rest_ensure_response( [\n            &#39;ok&#39;     =&gt; true,\n            &#39;valid&#39;  =&gt; false,\n            &#39;status&#39; =&gt; &#39;license_revoked&#39;,\n        ] );\n    }\n\n    $max_sites = (int) $license-&gt;max_sites;\n    $count     = mls_count_active_activations( $key_hash );\n\n    \/\/ last_verified_at \u66f4\u65b0\n    global $wpdb;\n    $now = current_time( &#39;mysql&#39; );\n    $wpdb-&gt;update(\n        $wpdb-&gt;prefix . &#39;mls_licenses&#39;,\n        [ &#39;last_verified_at&#39; =&gt; $now, &#39;updated_at&#39; =&gt; $now ],\n        [ &#39;id&#39; =&gt; $license-&gt;id ]\n    );\n\n    \/\/ activation \u30c1\u30a7\u30c3\u30af\n    $activation = mls_find_active_activation( $key_hash, $site_hash );\n    if ( $activation ) {\n        $wpdb-&gt;update(\n            $wpdb-&gt;prefix . &#39;mls_activations&#39;,\n            [ &#39;last_seen_at&#39; =&gt; $now ],\n            [ &#39;id&#39; =&gt; $activation-&gt;id ]\n        );\n        mls_write_log( &#39;validate&#39;, $key_last4, $normalized, &#39;ok&#39;, &#39;Valid&#39; );\n        return rest_ensure_response( [\n            &#39;ok&#39;        =&gt; true,\n            &#39;valid&#39;     =&gt; true,\n            &#39;status&#39;    =&gt; &#39;active&#39;,\n            &#39;site_count&#39; =&gt; $count,\n            &#39;max_sites&#39; =&gt; $max_sites,\n            &#39;domain&#39;    =&gt; $normalized,\n        ] );\n    }\n\n    mls_write_log( &#39;validate&#39;, $key_last4, $normalized, &#39;fail&#39;, &#39;Not activated for this site&#39; );\n    return rest_ensure_response( [\n        &#39;ok&#39;        =&gt; true,\n        &#39;valid&#39;     =&gt; false,\n        &#39;status&#39;    =&gt; &#39;not_activated_for_this_site&#39;,\n        &#39;site_count&#39; =&gt; $count,\n        &#39;max_sites&#39; =&gt; $max_sites,\n    ] );\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ deactivate\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction mls_action_deactivate( array $body ) {\n    $v = mls_validate_input( $body, [ &#39;license_key&#39;, &#39;site_url&#39; ] );\n    if ( is_wp_error( $v ) ) {\n        return mls_json_error( &#39;validation_error&#39;, $v-&gt;get_error_message(), 400 );\n    }\n\n    $license_key = $body[&#39;license_key&#39;];\n    $product_id  = mls_get_product_id( $body );\n    $site_url    = $body[&#39;site_url&#39;];\n\n    $normalized = mls_normalize_site_url( $site_url );\n    $key_hash   = mls_sha256( $license_key );\n    $site_hash  = mls_sha256( $normalized );\n    $key_last4  = substr( $license_key, -4 );\n\n    \/\/ Gumroad verify\uff08\u89e3\u9664\u6642\u3082\u78ba\u8a8d\uff09\n    $gumroad = mls_verify_gumroad( $license_key, $product_id );\n    if ( ! $gumroad[&#39;valid&#39;] ) {\n        mls_write_log( &#39;deactivate&#39;, $key_last4, $normalized, &#39;fail&#39;, &#39;Gumroad invalid&#39; );\n        return mls_json_error( &#39;license_invalid&#39;, &#39;\u30e9\u30a4\u30bb\u30f3\u30b9\u304c\u7121\u52b9\u3067\u3059&#39;, 403 );\n    }\n\n    global $wpdb;\n    $now = current_time( &#39;mysql&#39; );\n\n    $activation = mls_find_active_activation( $key_hash, $site_hash );\n    if ( $activation ) {\n        $wpdb-&gt;update(\n            $wpdb-&gt;prefix . &#39;mls_activations&#39;,\n            [ &#39;status&#39; =&gt; &#39;inactive&#39;, &#39;deactivated_at&#39; =&gt; $now ],\n            [ &#39;id&#39; =&gt; $activation-&gt;id ]\n        );\n    }\n\n    $count = mls_count_active_activations( $key_hash );\n    $license = mls_find_license( $key_hash, $product_id );\n    $max_sites = $license ? (int) $license-&gt;max_sites : 3;\n\n    mls_write_log( &#39;deactivate&#39;, $key_last4, $normalized, &#39;ok&#39;, &quot;Deactivated. remaining: {$count}\/{$max_sites}&quot; );\n\n    return rest_ensure_response( [\n        &#39;ok&#39;        =&gt; true,\n        &#39;status&#39;    =&gt; &#39;deactivated&#39;,\n        &#39;site_count&#39; =&gt; $count,\n        &#39;max_sites&#39; =&gt; $max_sites,\n    ] );\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ Gumroad verify\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction mls_verify_gumroad( string $license_key, string $product_id ): array {\n    \/\/ Gumroad verify API\u306b product_id\uff08Base64\u5f62\u5f0f\uff09\u3092\u9001\u308b\u3002API\u30ad\u30fc\u4e0d\u8981\u3002\n    $response = wp_remote_post( &#39;https:\/\/api.gumroad.com\/v2\/licenses\/verify&#39;, [\n        &#39;timeout&#39; =&gt; 10,\n        &#39;body&#39;    =&gt; [\n            &#39;product_id&#39;           =&gt; $product_id,\n            &#39;license_key&#39;          =&gt; $license_key,\n            &#39;increment_uses_count&#39; =&gt; &#39;false&#39;,\n        ],\n    ] );\n\n    if ( is_wp_error( $response ) ) {\n        return [ &#39;valid&#39; =&gt; false, &#39;reason&#39; =&gt; &#39;network_error&#39;, &#39;email&#39; =&gt; &#39;&#39;, &#39;purchase_id&#39; =&gt; &#39;&#39; ];\n    }\n\n    $code = wp_remote_retrieve_response_code( $response );\n    $data = json_decode( wp_remote_retrieve_body( $response ), true );\n\n    if ( $code !== 200 || empty( $data[&#39;success&#39;] ) ) {\n        $reason = $data[&#39;message&#39;] ?? &#39;invalid&#39;;\n        return [ &#39;valid&#39; =&gt; false, &#39;reason&#39; =&gt; $reason, &#39;email&#39; =&gt; &#39;&#39;, &#39;purchase_id&#39; =&gt; &#39;&#39; ];\n    }\n\n    $purchase = $data[&#39;purchase&#39;] ?? [];\n\n    \/\/ \u8fd4\u91d1\u30fb\u7d1b\u4e89\u30c1\u30a7\u30c3\u30af\n    if ( ! empty( $purchase[&#39;refunded&#39;] ) || ! empty( $purchase[&#39;disputed&#39;] ) || ! empty( $purchase[&#39;chargebacked&#39;] ) ) {\n        return [ &#39;valid&#39; =&gt; false, &#39;reason&#39; =&gt; &#39;refunded_or_disputed&#39;, &#39;email&#39; =&gt; &#39;&#39;, &#39;purchase_id&#39; =&gt; &#39;&#39; ];\n    }\n\n    return [\n        &#39;valid&#39;       =&gt; true,\n        &#39;reason&#39;      =&gt; &#39;&#39;,\n        &#39;email&#39;       =&gt; sanitize_email( $purchase[&#39;email&#39;] ?? &#39;&#39; ),\n        &#39;purchase_id&#39; =&gt; sanitize_text_field( $purchase[&#39;id&#39;] ?? $purchase[&#39;sale_id&#39;] ?? &#39;&#39; ),\n    ];\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u95a2\u6570\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\/** site_url \u3092\u6b63\u898f\u5316\u3059\u308b *\/\nfunction mls_normalize_site_url( string $url ): string {\n    $url = strtolower( trim( $url ) );\n    \/\/ scheme\u304c\u306a\u3051\u308c\u3070\u4ed8\u52a0\u3057\u3066parse_url\u3092\u901a\u3059\n    if ( ! preg_match( &#39;#^https?:\/\/#&#39;, $url ) ) {\n        $url = &#39;https:\/\/&#39; . $url;\n    }\n    $parsed = parse_url( $url );\n    $host   = $parsed[&#39;host&#39;] ?? $url;\n    \/\/ www. \u9664\u53bb\n    $host   = preg_replace( &#39;\/^www\\.\/&#39;, &#39;&#39;, $host );\n    return $host;\n}\n\n\/** SHA-256\u30cf\u30c3\u30b7\u30e5 *\/\nfunction mls_sha256( string $value ): string {\n    return hash( &#39;sha256&#39;, $value );\n}\n\n\/** \u30e9\u30a4\u30bb\u30f3\u30b9\u884c\u53d6\u5f97 *\/\nfunction mls_find_license( string $key_hash, string $product_id ) {\n    global $wpdb;\n    return $wpdb-&gt;get_row( $wpdb-&gt;prepare(\n        &quot;SELECT * FROM {$wpdb-&gt;prefix}mls_licenses WHERE license_key_hash=%s AND product_id=%s LIMIT 1&quot;,\n        $key_hash, $product_id\n    ) );\n}\n\n\/** \u30a2\u30af\u30c6\u30a3\u30d6\u306aactivation\u884c\u53d6\u5f97 *\/\nfunction mls_find_active_activation( string $key_hash, string $site_hash ) {\n    global $wpdb;\n    return $wpdb-&gt;get_row( $wpdb-&gt;prepare(\n        &quot;SELECT * FROM {$wpdb-&gt;prefix}mls_activations WHERE license_key_hash=%s AND site_hash=%s AND status=&#39;active&#39; LIMIT 1&quot;,\n        $key_hash, $site_hash\n    ) );\n}\n\n\/** \u30a2\u30af\u30c6\u30a3\u30d6\u306aactivation\u6570 *\/\nfunction mls_count_active_activations( string $key_hash ): int {\n    global $wpdb;\n    return (int) $wpdb-&gt;get_var( $wpdb-&gt;prepare(\n        &quot;SELECT COUNT(*) FROM {$wpdb-&gt;prefix}mls_activations WHERE license_key_hash=%s AND status=&#39;active&#39;&quot;,\n        $key_hash\n    ) );\n}\n\n\/** \u30ed\u30b0\u66f8\u304d\u8fbc\u307f *\/\nfunction mls_write_log( string $action, string $key_last4, string $domain, string $result, string $message ) {\n    global $wpdb;\n    $wpdb-&gt;insert( $wpdb-&gt;prefix . &#39;mls_logs&#39;, [\n        &#39;timestamp&#39;         =&gt; current_time( &#39;mysql&#39; ),\n        &#39;action&#39;            =&gt; $action,\n        &#39;license_key_last4&#39; =&gt; $key_last4,\n        &#39;domain&#39;            =&gt; $domain,\n        &#39;result&#39;            =&gt; $result,\n        &#39;message&#39;           =&gt; $message,\n    ] );\n\n    \/\/ \u30ed\u30b0\u304c10000\u884c\u3092\u8d85\u3048\u305f\u3089\u53e4\u3044\u884c\u3092\u524a\u9664\n    $count = (int) $wpdb-&gt;get_var( &quot;SELECT COUNT(*) FROM {$wpdb-&gt;prefix}mls_logs&quot; );\n    if ( $count &gt; 10000 ) {\n        $wpdb-&gt;query( &quot;DELETE FROM {$wpdb-&gt;prefix}mls_logs ORDER BY id ASC LIMIT 1000&quot; );\n    }\n}\n\n\/** \u5165\u529b\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3 *\/\nfunction mls_validate_input( array $body, array $required_keys ) {\n    \/\/ product_slug \u2192 product_id \u3078\u6b63\u898f\u5316\uff08amu-ai-publisher \u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u5bfe\u5fdc\uff09\n    if ( empty( $body[&#39;product_id&#39;] ) && ! empty( $body[&#39;product_slug&#39;] ) ) {\n        $body[&#39;product_id&#39;] = $body[&#39;product_slug&#39;];\n    }\n\n    foreach ( $required_keys as $key ) {\n        if ( empty( $body[ $key ] ) ) {\n            return new WP_Error( &#39;missing_field&#39;, &quot;\u5fc5\u9808\u30d1\u30e9\u30e1\u30fc\u30bf\u304c\u4e0d\u8db3\u3057\u3066\u3044\u307e\u3059: {$key}&quot; );\n        }\n    }\n\n    \/\/ product_id\u306e\u7167\u5408\u306fGumroad verify\u306b\u4efb\u305b\u308b\u305f\u3081\u3053\u3053\u3067\u306f\u30c1\u30a7\u30c3\u30af\u3057\u306a\u3044\n    return true;\n}\n\n\/** Gumroad verify\u7528\u306eproduct_id\u3092\u53d6\u5f97\uff08DB\u8a2d\u5b9a\u5024\u3092\u512a\u5148\uff09 *\/\nfunction mls_get_product_id( array $body ): string {\n    \/\/ DB\u306b\u8a2d\u5b9a\u3055\u308c\u305fProduct ID\u3092\u512a\u5148\uff08Gumroad verify\u7528\uff09\n    $configured = get_option( &#39;mls_product_id&#39;, &#39;&#39; );\n    if ( $configured ) {\n        return trim( $configured );\n    }\n    \/\/ \u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af: \u30ea\u30af\u30a8\u30b9\u30c8\u306eproduct_id or product_slug\n    if ( ! empty( $body[&#39;product_id&#39;] ) ) {\n        return sanitize_text_field( $body[&#39;product_id&#39;] );\n    }\n    return sanitize_text_field( $body[&#39;product_slug&#39;] ?? &#39;&#39; );\n}\n\n\/** \u30a8\u30e9\u30fc\u30ec\u30b9\u30dd\u30f3\u30b9 *\/\nfunction mls_json_error( string $code, string $message, int $status = 400 ) {\n    return new WP_REST_Response( [\n        &#39;ok&#39;      =&gt; false,\n        &#39;status&#39;  =&gt; $code,\n        &#39;message&#39; =&gt; $message,\n    ], $status );\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u7ba1\u7406\u753b\u9762: \u8a2d\u5b9a\u30da\u30fc\u30b8\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nadd_action( &#39;admin_menu&#39;, &#39;mls_admin_menu&#39; );\nfunction mls_admin_menu() {\n    add_options_page(\n        &#39;License Server \u8a2d\u5b9a&#39;,\n        &#39;License Server&#39;,\n        &#39;manage_options&#39;,\n        &#39;mls-settings&#39;,\n        &#39;mls_settings_page&#39;\n    );\n}\n\nadd_action( &#39;admin_init&#39;, &#39;mls_register_settings&#39; );\nfunction mls_register_settings() {\n    register_setting( &#39;mls_options&#39;, &#39;mls_product_id&#39;, [\n        &#39;sanitize_callback&#39; =&gt; function( $val ) { return trim( (string) $val ); },\n    ] );\n}\n\nfunction mls_settings_page() {\n    global $wpdb;\n    ?&gt;\n    &lt;div class=&quot;wrap&quot;&gt;\n        &lt;h1&gt;License Server \u8a2d\u5b9a&lt;\/h1&gt;\n        &lt;form method=&quot;post&quot; action=&quot;options.php&quot;&gt;\n            &lt;?php settings_fields( &#39;mls_options&#39; ); ?&gt;\n            &lt;table class=&quot;form-table&quot;&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;Gumroad Product ID&lt;\/th&gt;\n                    &lt;td&gt;\n                        &lt;input type=&quot;text&quot; name=&quot;mls_product_id&quot;\n                            value=&quot;&lt;?php echo esc_attr( get_option( &#39;mls_product_id&#39; ) ); ?&gt;&quot;\n                            class=&quot;regular-text&quot; \/&gt;\n                        &lt;p class=&quot;description&quot;&gt;Gumroad\u7ba1\u7406\u753b\u9762\u306e\u300cUse your product ID to verify licenses\u300d\u306eID\uff08\u4f8b: xxxxxxxxxxxxxxxxxxx-X==\uff09&lt;\/p&gt;\n                    &lt;\/td&gt;\n                &lt;\/tr&gt;\n            &lt;\/table&gt;\n            &lt;?php submit_button(); ?&gt;\n        &lt;\/form&gt;\n\n        &lt;h2&gt;\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8&lt;\/h2&gt;\n        &lt;code&gt;&lt;?php echo esc_url( rest_url( &#39;mls\/v1\/license&#39; ) ); ?&gt;&lt;\/code&gt;\n\n        &lt;h2&gt;\u30e9\u30a4\u30bb\u30f3\u30b9\u4e00\u89a7&lt;\/h2&gt;\n        &lt;?php\n        $licenses = $wpdb-&gt;get_results( &quot;SELECT * FROM {$wpdb-&gt;prefix}mls_licenses ORDER BY created_at DESC LIMIT 50&quot; );\n        if ( $licenses ) :\n        ?&gt;\n        &lt;table class=&quot;wp-list-table widefat striped&quot;&gt;\n            &lt;thead&gt;&lt;tr&gt;\n                &lt;th&gt;Key\u672b\u5c3e4\u6841&lt;\/th&gt;&lt;th&gt;Product ID&lt;\/th&gt;&lt;th&gt;Status&lt;\/th&gt;\n                &lt;th&gt;Email&lt;\/th&gt;&lt;th&gt;\u4f7f\u7528\u6570\/\u4e0a\u9650&lt;\/th&gt;&lt;th&gt;\u6700\u7d42\u78ba\u8a8d&lt;\/th&gt;\n            &lt;\/tr&gt;&lt;\/thead&gt;\n            &lt;tbody&gt;\n            &lt;?php foreach ( $licenses as $lic ) :\n                $count = mls_count_active_activations( $lic-&gt;license_key_hash );\n            ?&gt;\n                &lt;tr&gt;\n                    &lt;td&gt;****&lt;?php echo esc_html( $lic-&gt;license_key_last4 ); ?&gt;&lt;\/td&gt;\n                    &lt;td&gt;&lt;?php echo esc_html( $lic-&gt;product_id ); ?&gt;&lt;\/td&gt;\n                    &lt;td&gt;&lt;?php echo esc_html( $lic-&gt;status ); ?&gt;&lt;\/td&gt;\n                    &lt;td&gt;&lt;?php echo esc_html( $lic-&gt;gumroad_email ); ?&gt;&lt;\/td&gt;\n                    &lt;td&gt;&lt;?php echo esc_html( $count . &#39;\/&#39; . $lic-&gt;max_sites ); ?&gt;&lt;\/td&gt;\n                    &lt;td&gt;&lt;?php echo esc_html( $lic-&gt;last_verified_at ); ?&gt;&lt;\/td&gt;\n                &lt;\/tr&gt;\n            &lt;?php endforeach; ?&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n        &lt;?php else : ?&gt;\n        &lt;p&gt;\u307e\u3060\u30e9\u30a4\u30bb\u30f3\u30b9\u30c7\u30fc\u30bf\u304c\u3042\u308a\u307e\u305b\u3093\u3002&lt;\/p&gt;\n        &lt;?php endif; ?&gt;\n\n        &lt;h2&gt;\u6700\u8fd1\u306e\u30ed\u30b0&lt;\/h2&gt;\n        &lt;?php\n        $logs = $wpdb-&gt;get_results( &quot;SELECT * FROM {$wpdb-&gt;prefix}mls_logs ORDER BY id DESC LIMIT 50&quot; );\n        if ( $logs ) :\n        ?&gt;\n        &lt;table class=&quot;wp-list-table widefat striped&quot;&gt;\n            &lt;thead&gt;&lt;tr&gt;\n                &lt;th&gt;\u65e5\u6642&lt;\/th&gt;&lt;th&gt;Action&lt;\/th&gt;&lt;th&gt;Key\u672b\u5c3e4\u6841&lt;\/th&gt;&lt;th&gt;Domain&lt;\/th&gt;&lt;th&gt;Result&lt;\/th&gt;&lt;th&gt;Message&lt;\/th&gt;\n            &lt;\/tr&gt;&lt;\/thead&gt;\n            &lt;tbody&gt;\n            &lt;?php foreach ( $logs as $log ) : ?&gt;\n                &lt;tr&gt;\n                    &lt;td&gt;&lt;?php echo esc_html( $log-&gt;timestamp ); ?&gt;&lt;\/td&gt;\n                    &lt;td&gt;&lt;?php echo esc_html( $log-&gt;action ); ?&gt;&lt;\/td&gt;\n                    &lt;td&gt;****&lt;?php echo esc_html( $log-&gt;license_key_last4 ); ?&gt;&lt;\/td&gt;\n                    &lt;td&gt;&lt;?php echo esc_html( $log-&gt;domain ); ?&gt;&lt;\/td&gt;\n                    &lt;td style=&quot;color:&lt;?php echo $log-&gt;result === &#39;ok&#39; ? &#39;green&#39; : &#39;red&#39;; ?&gt;&quot;&gt;\n                        &lt;?php echo esc_html( $log-&gt;result ); ?&gt;&lt;\/td&gt;\n                    &lt;td&gt;&lt;?php echo esc_html( $log-&gt;message ); ?&gt;&lt;\/td&gt;\n                &lt;\/tr&gt;\n            &lt;?php endforeach; ?&gt;\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n        &lt;?php else : ?&gt;\n        &lt;p&gt;\u30ed\u30b0\u306f\u3042\u308a\u307e\u305b\u3093\u3002&lt;\/p&gt;\n        &lt;?php endif; ?&gt;\n    &lt;\/div&gt;\n    &lt;?php\n}<\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">\u81ea\u4f5c\u30d7\u30e9\u30b0\u30a4\u30f3<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u8a8d\u8a3c\u90e8\u5206\u306e\u5b9f\u88c5\u3060\u3051\u3001\u30b3\u30fc\u30c9\u3092\u516c\u958b\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-php\" data-lang=\"PHP\"><code>&lt;?php\n\/**\n * \u30e9\u30a4\u30bb\u30f3\u30b9\u8a8d\u8a3c\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\uff08\u516c\u958b\u30b5\u30f3\u30d7\u30eb\uff09\n *\n * \u8ca9\u58f2\u30d7\u30e9\u30b0\u30a4\u30f3\u5074\u306b\u7d44\u307f\u8fbc\u3080\u8a8d\u8a3c\u51e6\u7406\u3067\u3059\u3002\n * \u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u306e\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u306b activate \/ validate \/ deactivate \u3092\u9001\u4fe1\u3057\u307e\u3059\u3002\n *\n * File: inc\/license-client.php\n *\/\n\ndeclare(strict_types=1);\n\nif (! defined(&#39;ABSPATH&#39;)) {\n    exit;\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u30e9\u30a4\u30bb\u30f3\u30b9\u72b6\u614b\u306e\u7ba1\u7406\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction amu_ai_license_default_state(): array\n{\n    return [\n        &#39;status&#39;     =&gt; &#39;inactive&#39;,\n        &#39;last_check&#39; =&gt; &#39;&#39;,\n        &#39;domain&#39;     =&gt; &#39;&#39;,\n        &#39;site_count&#39; =&gt; 0,\n        &#39;max_sites&#39;  =&gt; 0,\n        &#39;message&#39;    =&gt; &#39;&#39;,\n    ];\n}\n\nfunction amu_ai_license_get_state(): array\n{\n    $state = get_option(&#39;amu_ai_license_state&#39;, []);\n    return wp_parse_args(is_array($state) ? $state : [], amu_ai_license_default_state());\n}\n\nfunction amu_ai_license_update_state(array $state): void\n{\n    $current = amu_ai_license_get_state();\n    update_option(&#39;amu_ai_license_state&#39;, array_merge($current, $state), false);\n}\n\nfunction amu_ai_license_reset_state(): void\n{\n    update_option(&#39;amu_ai_license_state&#39;, amu_ai_license_default_state(), false);\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u8a2d\u5b9a\u5024\u306e\u53d6\u5f97\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\/** \u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u306e\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8URL *\/\nfunction amu_ai_license_get_api_url(): string\n{\n    $settings = get_option(&#39;amu_ai_settings&#39;, []);\n    return untrailingslashit((string) ($settings[&#39;license_api_url&#39;] ?? &#39;&#39;));\n}\n\n\/** \u3053\u306e\u30b5\u30a4\u30c8\u306e\u30c9\u30e1\u30a4\u30f3\uff08\u6b63\u898f\u5316\u6e08\u307f\uff09 *\/\nfunction amu_ai_license_get_current_domain(): string\n{\n    $host = wp_parse_url(home_url(&#39;\/&#39;), PHP_URL_HOST);\n    $host = is_string($host) ? strtolower($host) : &#39;&#39;;\n    return preg_replace(&#39;\/^www\\.\/&#39;, &#39;&#39;, $host) ?? $host;\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u3078\u306e\u30ea\u30af\u30a8\u30b9\u30c8\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\/**\n * \u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u306b action \u3092\u9001\u4fe1\u3059\u308b\u5171\u901a\u95a2\u6570\n *\n * @param string $action  activate \/ validate \/ deactivate\n * @return array|WP_Error \u30ec\u30b9\u30dd\u30f3\u30b9JSON\u914d\u5217 or WP_Error\n *\/\nfunction amu_ai_license_request(string $action)\n{\n    $settings    = get_option(&#39;amu_ai_settings&#39;, []);\n    $license_key = (string) ($settings[&#39;license_key&#39;] ?? &#39;&#39;);\n    $api_url     = amu_ai_license_get_api_url();\n\n    if ($license_key === &#39;&#39; || $api_url === &#39;&#39;) {\n        return new WP_Error(&#39;amu_ai_license_missing_config&#39;, &#39;\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u307e\u305f\u306fAPI\u306eURL\u304c\u672a\u8a2d\u5b9a\u3067\u3059\u3002&#39;);\n    }\n\n    $response = wp_remote_post($api_url, [\n        &#39;timeout&#39; =&gt; 10,\n        &#39;headers&#39; =&gt; [&#39;Content-Type&#39; =&gt; &#39;application\/json; charset=utf-8&#39;],\n        &#39;body&#39;    =&gt; wp_json_encode([\n            &#39;action&#39;         =&gt; $action,\n            &#39;product_slug&#39;   =&gt; &#39;your-plugin-slug&#39;, \/\/ \u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u30b9\u30e9\u30c3\u30b0\u306b\u5909\u66f4\n            &#39;license_key&#39;    =&gt; $license_key,\n            &#39;site_url&#39;       =&gt; home_url(&#39;\/&#39;),\n            &#39;plugin_version&#39; =&gt; &#39;1.0.0&#39;, \/\/ \u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u30d0\u30fc\u30b8\u30e7\u30f3\u306b\u5909\u66f4\n        ]),\n    ]);\n\n    if (is_wp_error($response)) {\n        return $response;\n    }\n\n    $code = (int) wp_remote_retrieve_response_code($response);\n    $body = (string) wp_remote_retrieve_body($response);\n    $data = json_decode($body, true);\n\n    if ($code &lt; 200 || $code &gt;= 300 || ! is_array($data)) {\n        return new WP_Error(&#39;amu_ai_license_invalid_response&#39;, &#39;\u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u304b\u3089\u306e\u5fdc\u7b54\u304c\u4e0d\u6b63\u3067\u3059\u3002&#39;);\n    }\n\n    return $data;\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u30ec\u30b9\u30dd\u30f3\u30b9\u3092DB\u306b\u4fdd\u5b58\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction amu_ai_license_store_response(array $data, string $fallback_status = &#39;inactive&#39;): void\n{\n    amu_ai_license_update_state([\n        &#39;status&#39;     =&gt; (string) ($data[&#39;status&#39;] ?? $fallback_status),\n        &#39;last_check&#39; =&gt; current_time(&#39;mysql&#39;),\n        &#39;domain&#39;     =&gt; (string) ($data[&#39;domain&#39;] ?? amu_ai_license_get_current_domain()),\n        &#39;site_count&#39; =&gt; (int) ($data[&#39;site_count&#39;] ?? 0),\n        &#39;max_sites&#39;  =&gt; (int) ($data[&#39;max_sites&#39;] ?? 0),\n        &#39;message&#39;    =&gt; (string) ($data[&#39;message&#39;] ?? &#39;&#39;),\n    ]);\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u6709\u52b9\u6027\u30c1\u30a7\u30c3\u30af\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\/**\n * \u30e9\u30a4\u30bb\u30f3\u30b9\u304c\u6709\u52b9\u304b\u3069\u3046\u304b\u3092\u5224\u5b9a\u3059\u308b\n *\n * - status \u304c &#39;active&#39; \u3067\u3042\u308b\u3053\u3068\n * - \u6700\u7d42\u78ba\u8a8d\u304b\u30897\u65e5\u4ee5\u5185\u3067\u3042\u308b\u3053\u3068\uff08\u901a\u4fe1\u5931\u6557\u6642\u306e\u7336\u4e88\u671f\u9593\uff09\n *\n * \u6709\u6599\u6a5f\u80fd\u306e\u30ac\u30fc\u30c9\u306b\u4f7f\u3046: if ( ! amu_ai_license_is_recently_valid() ) { return; }\n *\/\nfunction amu_ai_license_is_recently_valid(): bool\n{\n    $state = amu_ai_license_get_state();\n\n    if (($state[&#39;status&#39;] ?? &#39;&#39;) !== &#39;active&#39;) {\n        return false;\n    }\n\n    $last_check = strtotime((string) ($state[&#39;last_check&#39;] ?? &#39;&#39;));\n    if (! $last_check) {\n        return false;\n    }\n\n    \/\/ 7\u65e5\u4ee5\u5185\u306a\u3089\u6709\u52b9\uff08\u30e9\u30a4\u30bb\u30f3\u30b9\u30b5\u30fc\u30d0\u30fc\u304c\u4e00\u6642\u7684\u306b\u30c0\u30a6\u30f3\u3057\u3066\u3082\u7336\u4e88\u3092\u6301\u3064\uff09\n    return $last_check &gt;= (time() - DAY_IN_SECONDS * 7);\n}\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u7ba1\u7406\u753b\u9762\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u30cf\u30f3\u30c9\u30e9\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\/** \u6709\u52b9\u5316\u30dc\u30bf\u30f3\u62bc\u4e0b *\/\nfunction amu_ai_license_handle_activate(): void\n{\n    if (! current_user_can(&#39;manage_options&#39;)) {\n        wp_die(&#39;Permission denied.&#39;);\n    }\n    check_admin_referer(&#39;amu_ai_license_activate&#39;);\n\n    $response = amu_ai_license_request(&#39;activate&#39;);\n\n    if (is_wp_error($response)) {\n        amu_ai_license_set_notice(&#39;error&#39;, $response-&gt;get_error_message());\n    } else {\n        amu_ai_license_store_response($response, &#39;inactive&#39;);\n        amu_ai_license_set_notice(&#39;success&#39;, (string) ($response[&#39;message&#39;] ?? &#39;\u30e9\u30a4\u30bb\u30f3\u30b9\u3092\u6709\u52b9\u5316\u3057\u307e\u3057\u305f\u3002&#39;));\n    }\n\n    wp_safe_redirect(wp_get_referer() ?: admin_url(&#39;options-general.php?page=your-plugin-license&#39;));\n    exit;\n}\nadd_action(&#39;admin_post_amu_ai_license_activate&#39;, &#39;amu_ai_license_handle_activate&#39;);\n\n\/** \u691c\u8a3c\u30dc\u30bf\u30f3\u62bc\u4e0b *\/\nfunction amu_ai_license_handle_validate(): void\n{\n    if (! current_user_can(&#39;manage_options&#39;)) {\n        wp_die(&#39;Permission denied.&#39;);\n    }\n    check_admin_referer(&#39;amu_ai_license_validate&#39;);\n\n    $response = amu_ai_license_request(&#39;validate&#39;);\n\n    if (is_wp_error($response)) {\n        amu_ai_license_set_notice(&#39;error&#39;, $response-&gt;get_error_message());\n    } else {\n        amu_ai_license_store_response($response, ! empty($response[&#39;valid&#39;]) ? &#39;active&#39; : &#39;inactive&#39;);\n        amu_ai_license_set_notice(&#39;success&#39;, (string) ($response[&#39;message&#39;] ?? &#39;\u30e9\u30a4\u30bb\u30f3\u30b9\u72b6\u614b\u3092\u78ba\u8a8d\u3057\u307e\u3057\u305f\u3002&#39;));\n    }\n\n    wp_safe_redirect(wp_get_referer() ?: admin_url(&#39;options-general.php?page=your-plugin-license&#39;));\n    exit;\n}\nadd_action(&#39;admin_post_amu_ai_license_validate&#39;, &#39;amu_ai_license_handle_validate&#39;);\n\n\/** \u89e3\u9664\u30dc\u30bf\u30f3\u62bc\u4e0b *\/\nfunction amu_ai_license_handle_deactivate(): void\n{\n    if (! current_user_can(&#39;manage_options&#39;)) {\n        wp_die(&#39;Permission denied.&#39;);\n    }\n    check_admin_referer(&#39;amu_ai_license_deactivate&#39;);\n\n    $response = amu_ai_license_request(&#39;deactivate&#39;);\n\n    if (is_wp_error($response)) {\n        amu_ai_license_set_notice(&#39;error&#39;, $response-&gt;get_error_message());\n    } else {\n        amu_ai_license_store_response($response, &#39;inactive&#39;);\n        amu_ai_license_set_notice(&#39;success&#39;, (string) ($response[&#39;message&#39;] ?? &#39;\u3053\u306e\u30b5\u30a4\u30c8\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u3092\u89e3\u9664\u3057\u307e\u3057\u305f\u3002&#39;));\n    }\n\n    wp_safe_redirect(wp_get_referer() ?: admin_url(&#39;options-general.php?page=your-plugin-license&#39;));\n    exit;\n}\nadd_action(&#39;admin_post_amu_ai_license_deactivate&#39;, &#39;amu_ai_license_handle_deactivate&#39;);\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u7ba1\u7406\u753b\u9762\u306e\u901a\u77e5\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction amu_ai_license_set_notice(string $type, string $message): void\n{\n    if (! is_user_logged_in()) {\n        return;\n    }\n    set_transient(&#39;amu_ai_license_notice_&#39; . get_current_user_id(), [\n        &#39;type&#39;    =&gt; $type,\n        &#39;message&#39; =&gt; $message,\n    ], 60);\n}\n\nfunction amu_ai_license_render_notice(): void\n{\n    if (! is_user_logged_in()) {\n        return;\n    }\n    $key    = &#39;amu_ai_license_notice_&#39; . get_current_user_id();\n    $notice = get_transient($key);\n    if (! is_array($notice) || empty($notice[&#39;message&#39;])) {\n        return;\n    }\n    delete_transient($key);\n    $class = ($notice[&#39;type&#39;] ?? &#39;success&#39;) === &#39;error&#39; ? &#39;notice-error&#39; : &#39;notice-success&#39;;\n    echo &#39;&lt;div class=&quot;notice &#39; . esc_attr($class) . &#39;&quot;&gt;&lt;p&gt;&#39; . esc_html((string) $notice[&#39;message&#39;]) . &#39;&lt;\/p&gt;&lt;\/div&gt;&#39;;\n}\nadd_action(&#39;admin_notices&#39;, &#39;amu_ai_license_render_notice&#39;);\n\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\/\/ \u6709\u6599\u6a5f\u80fd\u306e\u30ac\u30fc\u30c9\u4f8b\n\/\/ \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n\/**\n * AJAX\u30ea\u30af\u30a8\u30b9\u30c8\u306e\u5171\u901a\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u95a2\u6570\u306b\u30e9\u30a4\u30bb\u30f3\u30b9\u30c1\u30a7\u30c3\u30af\u3092\u8ffd\u52a0\u3059\u308b\u4f8b\n *\n * function your_plugin_validate_request(): void\n * {\n *     check_ajax_referer(&#39;your_plugin_nonce&#39;, &#39;nonce&#39;);\n *\n *     \/\/ \u30e9\u30a4\u30bb\u30f3\u30b9\u30c1\u30a7\u30c3\u30af\n *     if ( ! amu_ai_license_is_recently_valid() ) {\n *         wp_send_json_error([\n *             &#39;message&#39; =&gt; &#39;\u30e9\u30a4\u30bb\u30f3\u30b9\u304c\u6709\u52b9\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u30e9\u30a4\u30bb\u30f3\u30b9\u8a2d\u5b9a\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002&#39;\n *         ], 403);\n *     }\n *\n *     \/\/ \u4ee5\u964d\u306f\u901a\u5e38\u306e\u6a29\u9650\u30c1\u30a7\u30c3\u30af\u7b49\n * }\n *\/<\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">\u307e\u3068\u3081<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u306e\u69cb\u6210\u306e\u30dd\u30a4\u30f3\u30c8\u306f\u4ee5\u4e0b\u306e\u3068\u304a\u308a\u3067\u3059\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list is-style-num_circle\">\n<li><strong>\u5916\u90e8\u30b5\u30fc\u30d3\u30b9\u4e0d\u8981<\/strong>: \u30b7\u30f3\u30ec\u30f3\u30bf\u30eb\u30b5\u30fc\u30d0\u30fc\u306eWordPress\uff0bMySQL\u3060\u3051\u3067\u5b8c\u7d50<\/li>\n\n\n\n<li><strong>Gumroad\u306everify API\u306fAPI\u30ad\u30fc\u4e0d\u8981<\/strong>: product_id\u3068license_key\u3060\u3051\u3067\u691c\u8a3c\u3067\u304d\u308b<\/li>\n\n\n\n<li><strong>product_id\u306fURL\u30b9\u30e9\u30c3\u30b0\u3067\u306f\u306a\u304fBase64\u5f62\u5f0f<\/strong>: \u3053\u3053\u3092\u9593\u9055\u3048\u308b\u3068<code>That license does not exist<\/code>\u30a8\u30e9\u30fc\u306b\u306a\u308b<\/li>\n\n\n\n<li><strong>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u30d7\u30e9\u30b0\u30a4\u30f3\u304c\u7ba1\u7406\u753b\u9762\u306e\u8a2d\u5b9a\u4fdd\u5b58\u3092\u59a8\u3052\u308b\u3053\u3068\u304c\u3042\u308b<\/strong>: phpMyAdmin\u3067\u76f4\u63a5INSERT\u3059\u308b<\/li>\n\n\n\n<li><strong>Windows\u306eZIP\u306f\u30d0\u30c3\u30af\u30b9\u30e9\u30c3\u30b7\u30e5\u554f\u984c\u306b\u6ce8\u610f<\/strong>: Python\u3067\u518d\u30d1\u30c3\u30b1\u30fc\u30b8\u3059\u308b<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u30d7\u30e9\u30b0\u30a4\u30f3\u306e\u8ca9\u58f2\u898f\u6a21\u304c\u5c0f\u3055\u3044\u3046\u3061\u306f\u3001\u3053\u306e\u30b7\u30f3\u30d7\u30eb\u306a\u69cb\u6210\u3067\u5341\u5206\u306b\u6a5f\u80fd\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5c06\u6765\u7684\u306b\u306fGumroad Webhook\u3068\u9023\u643a\u3057\u305f\u5373\u6642\u8fd4\u91d1\u5bfe\u5fdc\u3084\u3001seats\u8ab2\u91d1\u9023\u52d5\u306a\u3069\u3082\u8ffd\u52a0\u3067\u304d\u308b\u62e1\u5f35\u6027\u3082\u6301\u305f\u305b\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u3053\u306e\u8a18\u4e8b\u3067\u7d39\u4ecb\u3057\u3066\u3044\u308b\u30b3\u30fc\u30c9\u306f\u3001GitHub\u306e<a href=\"https:\/\/github.com\/MOTOKI-LLC\/WAZA-code\/tree\/main\/wordpress-plugin-gumroad-license-authentication\">MOTOKI-LLC\/WAZA-code<\/a>\u306b\u3082\u307e\u3068\u3081\u3066\u3044\u307e\u3059\u3002<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>WordPress\u306e\u6709\u6599\u30d7\u30e9\u30b0\u30a4\u30f3\u3092Gumroad\u3067\u8ca9\u58f2\u3059\u308b\u5834\u5408\u3001\u30e9\u30a4\u30bb\u30f3\u30b9\u8a8d\u8a3c\u306e\u4ed5\u7d44\u307f\u3092\u3069\u3046\u4f5c\u308b\u304b\u304c\u8ab2\u984c\u306b\u306a\u308a\u307e\u3059\u3002 EDD\uff08Easy Digital Downloads\uff09\u3084Lemon Squeezy\u306a\u3069\u306e\u30b5\u30fc\u30d3\u30b9\u3092\u4f7f [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":27861,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"swell_btn_cv_data":"","book_review_data":"","footnotes":"","ssp_meta_title":"Gumroad\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u8a8d\u8a3c\u3092\u81ea\u4f5cWP\u306b\u5b9f\u88c5","ssp_meta_description":"Gumroad\u306e\u30e9\u30a4\u30bb\u30f3\u30b9\u30ad\u30fc\u691c\u8a3cAPI\u3068\u81ea\u4f5cWordPress REST API\u30b5\u30fc\u30d0\u30fc\u3092\u9023\u643a\u3055\u305b\u3001\u30d7\u30e9\u30b0\u30a4\u30f3\u8a8d\u8a3c\u3092\u5b9f\u88c5\u3059\u308b4\u3064\u306e\u30b9\u30c6\u30c3\u30d7\u3092\u89e3\u8aac\u3002\u6700\u59273\u30b5\u30a4\u30c8\u307e\u3067\u306e\u7ba1\u7406\u65b9\u6cd5\u3084\u3001\u8a8d\u8a3c\u7528Product ID\u306e\u78ba\u8a8d\u624b\u9806\u3092\u63b2\u8f09\u3057\u3066\u3044\u307e\u3059\u3002"},"categories":[111],"tags":[],"area":[],"class_list":["post-27850","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-plugin"],"_links":{"self":[{"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/posts\/27850","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/comments?post=27850"}],"version-history":[{"count":4,"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/posts\/27850\/revisions"}],"predecessor-version":[{"id":29005,"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/posts\/27850\/revisions\/29005"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/media\/27861"}],"wp:attachment":[{"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/media?parent=27850"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/categories?post=27850"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/tags?post=27850"},{"taxonomy":"area","embeddable":true,"href":"https:\/\/motoki-design.co.jp\/wordpress\/wp-json\/wp\/v2\/area?post=27850"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}