service = $service; }
/** Attach only authenticated forms and scoped screens. */
public function register() {
add_filter( 'devoynx_rewards_admin_tabs', array( $this, 'tabs' ) );
add_action( 'devoynx_rewards_admin_tab', array( $this, 'render_admin' ) );
add_action( 'admin_post_devoynx_rewards_spin_settings', array( $this, 'save' ) );
add_action( 'admin_post_devoynx_rewards_spin', array( $this, 'play' ) );
add_action( 'devoynx_rewards_wallet_after_activity', array( $this, 'render_wallet' ), 25 );
}
/**
* Add one useful merchant destination.
*
* @param array $tabs Existing navigation.
* @return array
*/
public function tabs( $tabs ) {
$tabs['spin'] = __( 'Spin campaign', 'devoynx-rewards' );
return $tabs; }
/** Save validated configuration while holding the same campaign lock as spins. */
public function save() {
if ( 'POST' !== sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ?? '' ) ) || ! current_user_can( 'manage_devoynx_rewards' ) ) {
wp_die( esc_html__( 'Permission denied.', 'devoynx-rewards' ), 403 ); }
check_admin_referer( 'devoynx_rewards_spin_settings' );
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Every field is allowlisted and bounded by the service validator before saving.
$settings = $this->service->sanitize( isset( $_POST['spin'] ) ? wp_unslash( $_POST['spin'] ) : array() );
if ( ! is_wp_error( $settings ) ) {
try {
( new ProgramStore() )->locked(
'spin:campaign',
'spin_campaign',
static function ( $row ) use ( $settings ) {
update_option( Spin::OPTION, $settings, false );
if ( 'new' === $row['status'] ) {
( new ProgramStore() )->save(
$row,
array(
'spent' => 0,
'spins' => 0,
),
'active'
); }
}
);
} catch ( \Throwable $error ) {
$settings = $this->service->error( 'failed' ); }
}
wp_safe_redirect(
add_query_arg(
array(
'page' => 'devoynx-rewards',
'tab' => 'spin',
'spin_saved' => is_wp_error( $settings ) ? 'invalid' : 'saved',
),
admin_url( 'admin.php' )
)
);
exit;
}
/** Mutate only on an authenticated, nonce-verified POST, never on a shareable GET. */
public function play() {
if ( 'POST' !== sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ?? '' ) ) || ! get_current_user_id() ) {
wp_die( esc_html__( 'Permission denied.', 'devoynx-rewards' ), 403 ); }
check_admin_referer( 'devoynx_rewards_spin' );
$request = isset( $_POST['spin_request'] ) && is_string( $_POST['spin_request'] ) ? sanitize_text_field( wp_unslash( $_POST['spin_request'] ) ) : '';
$result = $this->service->spin( get_current_user_id(), $request );
$query = is_wp_error( $result ) ? array( 'spin_error' => $result->get_error_code() ) : array( 'spin_result' => $request );
wp_safe_redirect( add_query_arg( $query, wc_get_account_endpoint_url( 'rewards' ) ) . '#devoynx-spin' );
exit;
}
/**
* Render actual consumption, safeguards and every configured outcome.
*
* @param string $tab Current merchant tab.
*/
public function render_admin( $tab ) {
if ( 'spin' !== $tab || ! current_user_can( 'manage_devoynx_rewards' ) ) {
return; }
$config = $this->service->settings();
$campaign = $this->service->campaign();
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only result on a capability-gated screen.
$saved = isset( $_GET['spin_saved'] ) ? sanitize_key( wp_unslash( $_GET['spin_saved'] ) ) : '';
$fields = array(
'budget' => array( __( 'Lifetime campaign point budget', 'devoynx-rewards' ), 100000000 ),
'max_spins' => array( __( 'Lifetime campaign spin limit', 'devoynx-rewards' ), 100000 ),
'per_customer' => array( __( 'Lifetime spins per account', 'devoynx-rewards' ), 100 ),
'interval_days' => array( __( 'Days between spins', 'devoynx-rewards' ), 365 ),
'account_age_days' => array( __( 'Minimum account age in days', 'devoynx-rewards' ), 365 ),
);
?>
service->error( 'configuration' )->get_error_message() ); ?>
service->error( $error )->get_error_message() ); ?>
time() ) : ?>
service->error( $reason )->get_error_message() ); ?>