'', 'sub_id' => '', 'period_end' => 0, ) ); if ( ! Orders::mark_paid( $order_id, $args['gateway_ref'] ) ) { return false; } $order = Orders::get( $order_id ); $plan = Plans::get( $order->plan_id ); if ( $plan && 'upgrade' === $plan['type'] ) { if ( $order->post_id ) { Posts::apply_upgrade( $order->post_id, $plan ); } } elseif ( $plan ) { $entitlement_id = self::grant( $order, $plan, $args ); if ( $order->post_id ) { self::use_entitlement( $entitlement_id, $order->post_id ); } } do_action( 'postfee_order_paid', $order, $plan ); return true; } private static function grant( $order, $plan, $args ) { $credits = 1; $expires = null; if ( 'package' === $plan['type'] ) { $credits = max( 1, $plan['credits'] ); if ( $plan['credit_days'] ) { $expires = postfee_now( $plan['credit_days'] * DAY_IN_SECONDS ); } } elseif ( 'subscription' === $plan['type'] ) { $credits = $plan['credits'] ? $plan['credits'] : null; $period_end = $args['period_end'] ? (int) $args['period_end'] : strtotime( '+1 ' . $plan['interval'] ); // One day of grace so a renewal payment has time to arrive. $expires = gmdate( 'Y-m-d H:i:s', $period_end + DAY_IN_SECONDS ); } $entitlement_id = Entitlements::grant( array( 'user_id' => $order->user_id, 'plan_id' => $plan['id'], 'order_id' => $order->id, 'credits_total' => $credits, 'listing_days' => $plan['listing_days'], 'featured_days' => $plan['featured_days'], 'gateway' => $order->gateway, 'gateway_sub_id' => (string) $args['sub_id'], 'expires_at' => $expires, ) ); // Subscribing again brings back posts taken down when the last one ended. if ( 'subscription' === $plan['type'] && $entitlement_id ) { Posts::resume( Entitlements::get( $entitlement_id ) ); } return $entitlement_id; } /** * Closes a subscription the gateway reports as over. */ public static function subscription_ended( $entitlement, $status = 'cancelled' ) { Entitlements::update( $entitlement->id, array( 'status' => $status ) ); Posts::suspend( $entitlement ); do_action( 'postfee_subscription_ended', $entitlement ); } public static function use_entitlement( $entitlement_id, $post_id ) { $entitlement = Entitlements::get( $entitlement_id ); if ( ! $entitlement || ! Entitlements::consume( $entitlement_id ) ) { return false; } Posts::activate( $post_id, $entitlement ); return true; } /** * Pays for a post with a credit its author already holds, if any. */ public static function try_credit( $post_id ) { $post = get_post( $post_id ); if ( ! $post ) { return false; } $entitlement = Entitlements::find_usable( $post->post_author, $post->post_type ); return $entitlement ? self::use_entitlement( $entitlement->id, $post_id ) : false; } /** * Gives back the post a deleted post used, if the plan it came from is * still running. */ public static function return_credit( $post_id ) { $entitlement = Entitlements::get( (int) get_post_meta( $post_id, '_postfee_entitlement_id', true ) ); if ( ! $entitlement || ! in_array( $entitlement->status, array( 'active', 'cancelling' ), true ) ) { return false; } // Posts paid for before plans had this setting are always given back. $windows = Plans::return_windows(); $window = get_post_meta( $post_id, '_postfee_return_window', true ); $seconds = isset( $windows[ $window ] ) ? $windows[ $window ][1] : null; $live_at = (int) get_post_meta( $post_id, '_postfee_live_at', true ); if ( 0 === $seconds || ( null !== $seconds && $live_at && time() - $live_at > $seconds ) ) { return false; } $plan = Plans::get( $entitlement->plan_id ); if ( $plan ? 'subscription' === $plan['type'] : '' !== $entitlement->gateway_sub_id ) { // Subscription posts are counted per billing period, so only a // post from the current period can be given back. $paid_at = (int) get_post_meta( $post_id, '_postfee_paid_at', true ); $period_start = $plan && $entitlement->expires_at ? strtotime( '-1 ' . $plan['interval'], strtotime( $entitlement->expires_at . ' UTC' ) - DAY_IN_SECONDS ) : 0; if ( ! $paid_at || ! $period_start || $paid_at < $period_start ) { return false; } } if ( ! Entitlements::release( $entitlement->id ) ) { return false; } // Unlinking the post keeps it from giving the same post back twice. delete_post_meta( $post_id, '_postfee_entitlement_id' ); do_action( 'postfee_credit_returned', $post_id, $entitlement ); return true; } /** * Records a subscription renewal and resets the period's credits. */ public static function subscription_renewed( $entitlement, $period_end, $gateway_ref, $amount, $currency ) { if ( $gateway_ref && Orders::find_by_ref( $entitlement->gateway, $gateway_ref ) ) { return false; } $plan = Plans::get( $entitlement->plan_id ); Orders::create( array( 'user_id' => $entitlement->user_id, 'plan_id' => $entitlement->plan_id, 'plan_type' => 'subscription', /* translators: %s: plan name */ 'description' => sprintf( __( 'Renewal: %s', 'postfee' ), $plan ? $plan['name'] : '#' . $entitlement->plan_id ), 'amount' => (int) $amount, 'currency' => strtoupper( $currency ), 'gateway' => $entitlement->gateway, 'gateway_ref' => $gateway_ref, 'status' => 'paid', 'paid_at' => postfee_now(), ) ); Entitlements::renew( $entitlement->id, $period_end ? (int) $period_end : strtotime( '+1 ' . ( $plan ? $plan['interval'] : 'month' ) ) ); Posts::resume( $entitlement ); do_action( 'postfee_subscription_renewed', $entitlement ); return true; } /** * Takes back what a refunded order bought: unused credits are revoked and * the post the order paid for goes back to awaiting payment. */ public static function order_refunded( $order_id ) { $order = Orders::get( $order_id ); if ( ! $order || 'paid' !== $order->status ) { return false; } Orders::update( $order_id, array( 'status' => 'refunded' ) ); foreach ( Entitlements::find_by_order( $order_id ) as $entitlement ) { Entitlements::update( $entitlement->id, array( 'status' => 'revoked' ) ); if ( $order->post_id && (int) get_post_meta( $order->post_id, '_postfee_entitlement_id', true ) === (int) $entitlement->id ) { Posts::deactivate( $order->post_id ); } } do_action( 'postfee_order_refunded', $order ); return true; } }