Forum
Perch Members - One Use Links
Hi All,
I am building an online shop using the official perch_shop which is capable of selling both consignment and digital products.
One of the issues I have encountered revolves around allowing customers to purchase digital products as a guest (i.e not creating a username/password combo) and how to securely allow a customer to reset their password. To resolve this I thought that implementing a one use code system would be the right way to go, thus authenticating the customer without exposing usable information unnecessarily in an email.
The code I have written is currently extending the PerchShop_Runtime
and is as follows:
Code
PHP
class CustomShop_MembersRuntime extends PerchShop_Runtime
{
private static $instance;
private $timeout;
public static function fetch()
{
if (!isset(self::$instance)) self::$instance = new CustomShop_MembersRuntime ;
return self::$instance;
}
/**
*
* Delete a unique code
*
* @param $memberId int
* @param $code string
* @return bool
*
*/
public function deleteCode($memberID, $code)
{
// Create DB handle
$db = PerchDB::fetch();
// Create delete query
$sql = "DELETE FROM `shop_codes` WHERE `memberID` = '".$memberID."' AND `code` = '".$code."' LIMIT 1";
$result = $db->execute($sql);
if ($result) {
return "This code has now been invalidated";
}
return "Error, code was not deleted";
}
/**
*
* Create a unique code
*
* @param $memberID int
* @return $code string
*
*/
private function createCode($memberID)
{
// Create a new code
$code = hash("sha1",uniqid($memberID, true));
// return a 20 character string
return $code;
}
/**
*
* Assign a code to a specific member
*
* @param $memberID int
* @param $code_type int 1
* @return bool
*
*/
public function assignCode($memberID, $code_type = 1)
{
// Create DB handle
$db = PerchDB::fetch();
// Create the code
$code = $this->createCode($memberID);
if (!is_int($code_type)) {
$code_type = intval($code_type);
}
// Create expiry information
$DT = new DateTime;
$now = $DT->format("Y-m-d H:m:s");
$DT->modify('+1 day');
$expires = $DT->format("Y-m-d H:m:s");
// Create insert query
$sql = "INSERT INTO `shop_codes` SET `memberID`='".$memberID."', `code`='".$code."', `code_type` = ".$code_type.", `expires_at` = '".$expires."', `created_at` = '".$now."' ";
// Assign a code to a given member
$result = $db->execute($sql);
if ($result) {
// If the query runs, return true
return true;
}
// If the query errors, return false
return false;
}
/**
*
* Remove all codes not used within the last 24hrs
*
* @return $result
*
*/
public function clearUnusedCodes()
{
// Create DB handle
$db = PerchDB::fetch();
// Get the current time
$DT = new DateTime;
$now = $DT->format("Y-m-d H:m:s");
// Create the selective delete query
$sql = "DELETE FROM `shop_codes` WHERE `expires_at` < '".$now."'";
// Delete all codes which have timed out within the last 24hrs
$result = $db->execute($sql);
if ($result) {
// If the query runs, return true
return "Codes have been deleted";
}
// If the query errors, return false
return "No codes deleted";
}
}
SQL
TABLES
#### UP ####
CREATE TABLE `shop_code_types` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`code_type` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `shop_codes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`memberID` int(10) NOT NULL,
`code` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`code_type` int(2) NOT NULL,
`expires_at` datetime DEFAULT NULL,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `fk_shop_code_types_id` (`code_type`),
CONSTRAINT `fk_shop_code_types_id` FOREIGN KEY (`code_type`) REFERENCES `shop_code_types` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
#### DOWN ####
DROP TABLE `shop_codes`;
DROP TABLE `shop_code_types`;
DATA
INSERT INTO `shop_code_type` (`id`,`code_type`) VALUES (1,"Password Reset");
INSERT INTO `shop_code_type` (`id`,`code_type`) VALUES (2,"Download Link");
Discussion/Questions
With a little modification this code has the basics to generate and delete codes based upon details gathered from the perch2_members
table (in this case memberID). For password resets, I also plan on using information drawn from either the perch2_members
or perch2_shop_customers
tables for further pieces of authentication before allowing the user to reset their password. I have yet to start coding for the download link scenario as I would like to finish the password functionality first.
My problems start when it comes to combining the functionality shown above with the existing perch_members
and perch_shop
addons. I need to find ways of resolving the following:
1) How to extend/overwrite the perch_members\runtime.php
->perch_members_form_handler()
method to replace the exisiting "reset" case.
2) How to integrate the use one time links methods with the perch_members addon functionality.
Any feedback and/or suggestions on how this could be improved or replaced by existing perch functionality would be very much appreciated!
We've been rolling password reset tokens throughout the product, and Members will surely be next.
I'm not sure I really follow the other use case.
It should go without saying that extending private classes on beta software is a terrible idea! If you're on the beta it'd be better to suggest your use case and help shape the product to achieve the sort of thing you need. That's the point of the programme.
Hi Drew,
Thanks for getting back to me on this. The second use case was written in a hurry and without much thought about specifics so i'll expand on it here.
The main scenario I am trying to cover is as follows:
I would like to be able to send them a one use link which will authenticate them and download the resource before expiring. However, just having the ability to generate one time links within the members app would be handy for a number of scenarios.
On your last point, I agree that extending beta software is pretty terrible idea and wherever possible I am trying to avoid extending and modifying perch code. However I am under time pressure to get required functionality finished before launch of our e-commerce site.
There are a number of suggestions I would like to put forwards but I only have access to the general slack channel at this time. If you could send me an invite it would be very much appreciated.
If you're not on the beta, where did you get the software from?