Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

JoshWilley's avatar

Enforce Contracts through an API

I have two different packages that need to communicate with each other, but don't need to know anything about one another.

I'm not quite sure how to accomplish this, I'll let the code explain:

Payable Interface:

interface Payable {

    /**
     * Get the payment properties associated with the model.
     *
     * @return \JoshWilley\Payment\Models\Payment
     */
    public function payable();
} 

Payment Interface:

interface Payment {

    /**
     * Charge the customer.
     * 
     * @param Payable $model
     * @param array $data
     * @return mixed
     */
    public function charge(Payable $model, array $data);
} 

Form Model:

class Form extends \Eloquent implements Payable {

    /**
     * Name of the table in the database
     *
     * @var string
     */
    protected $table = 'Forms';

    /**
     * A form has many fields
     *
     * @return mixed
     */
    public function fields()
    {
        return $this->hasMany('JoshWilley\Forms\FormField');
    }

    /**
     * A form has many submissions
     *
     * @return mixed
     */
    public function submissions()
    {
        return $this->hasMany('JoshWilley\Forms\FormSubmission');
    }

    public function payable()
    {
        return $this->belongsTo('JoshWilley\Payment\Models\Payment', 'payment_id');
    }
} 

PaymentApiController:

class PaymentApiController extends \BaseController {

    /**
     * @var \JoshWilley\Payment\Interfaces\Payment
     */
    private $payment;

    /**
     * @param Payment $payment
     */
    public function __construct(Payment $payment)
    {
        parent::__construct();
        $this->payment = $payment;
    }

    /**
     * Charge the customer.
     * 
     * @return mixed
     */
    public function store()
    {
        // This method needs to receive an instance of the "Payable" interface.
        return $this->payment->charge(Input::all());
    }

} 

Stripe-specific payment implementation:

class StripePayments implements Payment {

    /**
     * Instantiate with Stripe's API Key
     */
    public function __construct()
    {
        $apiKey = Config::get('stripe.secret_key') ?: Config::get('payment::stripe.secret_key');
        Stripe::setApiKey($apiKey);
    }

    /**
     * Charge the customer.
     *
     * @param Payable $model
     * @param array $data
     * @return mixed|void
     */
    public function charge(Payable $model, array $data)
    {
        try
        {
            return Stripe_Charge::create([
                'amount'      => $model->payable->amount,
                'currency'    => 'usd',
                'description' => $data['payment-stripe-email'],
                'card'        => $data['payment-stripe-token']
            ]);
        } catch(Stripe_CardError $e)
        {
            dd('Card was declined');
        }
    }
}

Okay, so here's my issue... Above, you'll see that the method on the PaymentApiController needs to receive an instance of the "Payable" interface. This means that the Stripe implementation of accepting payments can just get the "amount" property off of the model, as illustrated here:

/**
     * Charge the customer.
     *
     * @param Payable $model
     * @param array $data
     * @return mixed|void
     */
    public function charge(Payable $model, array $data)
    {
        try
        {
            return Stripe_Charge::create([
                'amount'      => $model->payable->amount,
                'currency'    => 'usd',
                'description' => $data['payment-stripe-email'],
                'card'        => $data['payment-stripe-token']
            ]);
        } catch(Stripe_CardError $e)
        {
            dd('Card was declined');
        }
    }

However, this method gets hit with a Javascript ajax call. How do I get my Payment package to know about the Form model without explicitly passing anything to it like class names, ids, etc.

Please let me know if you need any clarity, as I may not have painted the picture properly.

0 likes
1 reply
clevonnoel's avatar

You can do something like this

class Form extends \Eloquent implements Payable {

        /// use PayableTrait; 

        // you can refractor this to the a PayableTrait
        public function payment()
        {
             return new StripPayments($this);
        }
}
class StripePayments implements Payment {

    protected $payable;

    /**
     * Instantiate with Stripe's API Key
     */
    public function __construct(Payable $payable)
    {
        $this->payable = $payable;
        $apiKey = Config::get('stripe.secret_key') ?: Config::get('payment::stripe.secret_key');
        Stripe::setApiKey($apiKey);
    }

    /**
     * Charge the customer.
     *
     * @param Payable $model
     * @param array $data
     * @return mixed|void
     */
    public function charge(array $data)
    {
        try
        {
            return Stripe_Charge::create([
                'amount' => $this->payable->amount,
                'currency' => 'usd',
                'description' => $data['payment-stripe-email'],
                'card' => $data['payment-stripe-token']
            ]);
        } catch(Stripe_CardError $e)
        {
            dd('Card was declined');
        }
    }
}

And for your controller something like this

class PaymentApiController extends \BaseController {


    public function __construct()
    {

    }

    /**
     * Charge the customer.
     * 
     * @return mixed
     */
    public function store()
    {
        $form = Form::create(Input::all());

        return $form->payment()->charge(Input::only('token', 'payment-stripe-email', 'payment-stripe-token'));
    }

} 

Hope this is what you looking for

Please or to participate in this conversation.