campaigns->find_by_slug( $slug ); if ( ! $campaign ) { return new WP_Error( 'cnwscoupons_campaign_missing', __( 'The campaign does not exist.', 'webstyle-coupons' ) ); } if ( $campaign->is_shared() ) { return new WP_Error( 'cnwscoupons_campaign_shared', __( 'This campaign has one shared code and issues no personal codes.', 'webstyle-coupons' ) ); } $existing = $this->codes->find_for( $campaign->id, $email ); if ( $existing ) { return (string) $existing->code; } if ( ! $campaign->is_active() ) { return new WP_Error( 'cnwscoupons_campaign_ended', __( 'The campaign has ended and issues no new codes.', 'webstyle-coupons' ) ); } if ( $campaign->max_codes > 0 && $this->codes->count_personal( $campaign->id ) >= $campaign->max_codes ) { return new WP_Error( 'cnwscoupons_campaign_full', __( 'The campaign has issued all its codes.', 'webstyle-coupons' ) ); } $expires_at = self::expires_at( $campaign, time() ); for ( $attempt = 0; $attempt < self::ATTEMPTS; $attempt++ ) { $code = CodeGenerator::generate( $campaign->code_prefix ); if ( $this->taken( $code ) ) { continue; } if ( $this->codes->insert( $campaign->id, $code, $email, $source, $expires_at ) ) { return $code; } // A parallel request may have issued this recipient's code in the meantime. $existing = $this->codes->find_for( $campaign->id, $email ); if ( $existing ) { return (string) $existing->code; } } return new WP_Error( 'cnwscoupons_issue_failed', __( 'No code could be issued. Try again.', 'webstyle-coupons' ) ); } /** * When a code issued now expires, in UTC (Y-m-d H:i:s), or null for never. * * A code is valid to the end of its last day in the site's time zone. With both an end date and * a number of days, the earlier one wins. * * @param Campaign $campaign Campaign. * @param int $now Unix timestamp of issue. */ public static function expires_at( Campaign $campaign, int $now ): ?string { $zone = wp_timezone(); $dates = array(); if ( '' !== $campaign->expires_on ) { $dates[] = new \DateTimeImmutable( $campaign->expires_on . ' 23:59:59', $zone ); } if ( $campaign->valid_days > 0 ) { $dates[] = ( new \DateTimeImmutable( '@' . $now ) )->setTimezone( $zone )->modify( '+' . $campaign->valid_days . ' days' )->setTime( 23, 59, 59 ); } if ( ! $dates ) { return null; } return min( $dates )->setTimezone( new \DateTimeZone( 'UTC' ) )->format( 'Y-m-d H:i:s' ); } /** * A code must be unique among our codes and among the shop's own WooCommerce coupons. * * @param string $code Candidate code. */ private function taken( string $code ): bool { return $this->codes->code_exists( $code ) || ( function_exists( 'wc_get_coupon_id_by_code' ) && wc_get_coupon_id_by_code( $code ) ); } }