/** * bid on an item * only works with those listings that set an opening bid (even if that amount is zero). * We use the proxy-bid system here, as used by ebay: * @see http://en.wikipedia.org/wiki/Proxy_bid * the winning bidder pays the price of the second-highest bid plus the step */ public function bid($id, $bid, array $data = NULL) { // normalize the data. $data = new Store\KVP($data); // create an internal transaction if no transaction has been passed in. Transaction::start(); try { // we assume the current user is always the bidder $bidder = $this->user(); // if no bidder was passed into the constructor, blow up. if (!Souk\Util::validatePositiveInteger($bidder)) { throw new Exception('invalid bidder', $bidder); } // get a row lock on the listing. $listing = $this->get($id, TRUE); if (!$listing || !$listing->id) { throw new Exception('not found', $id); } // need the current time to do some comparisons. $ts = Souk\util::now(); // don't go anywhere if the bidding is already closed. if ($listing->closed) { throw new Exception('closed', $listing); } // can't let the seller bid on the listing. if ($listing->seller == $bidder) { throw new Exception('invalid bidder', $listing); } // step is set when it is a biddable item. if it isn't there, don't allow bidding. if ($listing->step < 1) { throw new Exception('buy only', $listing); } // has time expired on this listing? if ($listing->expires <= $ts) { throw new Exception('expired', $listing); } // make sure we bid enough to challenge the current bid level. // if proxy bidding is enabled we still might not win the bid, // but at least we pushed it up a bit. if ($listing->bid + $listing->step > $bid) { throw new Exception('too low', $listing); } // keep a pristine copy of the listing internally so other wrapper classes can compare // afterward and see what changes were made. // The Souk\stockpile adapter especially needs this so it can return escrowed bids // to the previous bidder. $listing->setPriorState($listing); // if proxy bidding is enabled, this gets a little more complicated. // proxy bidding is where you bid the max you are willing to pay, but only pay // one step above the previous bidder's level. // This is how ebay runs its auction site. // this means when you bid, we track your max amount you are willing to spend, but only // bid the minimum. When the next bid comes in, we automatically up your bid for you // until you go over your max amount and someone else takes the lead. // this approach makes the escrow system more efficient as well since it can excrow your // maximum amount all at once, and then refund when you get outbid or refund the difference // if you get it for a lower bid. if ($data->enable_proxy) { // looks like the previous bidder got outbid. // track their maximum amount, and set the bid based on one step above the previous bid. if ($bid >= $listing->proxybid + $listing->step) { $listing->bid = $listing->proxybid + $listing->step; $listing->proxybid = $bid; $listing->bidder = $bidder; $listing->bidcount = $listing->bidcount + 1; // the other bidder is still the winner of the bid. our bid didn't go over their // max bid amount. Bump up their bid amount to what we bid, and increment the // bid count by 2, since we bid on it, and they bid back. } else { $listing->bid = $bid; $listing->bidcount = $listing->bidcount + 2; } // in this case, not a proxy bid system, just a straight up english auction. // don't worry about previous bidder. we know we bid more than the previous bidder, // so pump up the bid to whatever we passed in. } else { $listing->bid = $bid; $listing->bidder = $bidder; $listing->bidcount = $listing->bidcount + 1; } $listing->touch = $ts; $this->storage()->bid($listing); Transaction::commit(); // done. return $listing; // something went wrong ... } catch (Exception $e) { // revert the transaction ... // if it was created internally, remove it. Transaction::rollback(); // toss the exception again. throw $e; } }
/** * what is the item id of the currency, in stockpile? */ protected function currencyId() { $id = $this->binder->currencyId(); if (!Util::validatePositiveInteger($id)) { throw new Exception('invalid currency id', $id); } return $id; }