みんなの「作ってみた」

個人開発のサービスを売買できる「個人開発のフリマ」を作りました

2019/05/27

svfreerider
svfreerider
エンジニアではないです!素人なんで、お手柔らかにお願いします。

個人開発のフリマは、個人開発やスタートアップで開発したプロダクトをフリマ感覚で売買できるサービスです。

以前、個人開発者の知見を集めたい!「開発会議」を作りましたを投稿させていただき、200名以上の個人開発者の方々にご登録いただき、より良いサービスづくりをしていく為にインタビューを始めました。そのお話をお伺いする中で、自分のサービスで食べていく以外に「サービスの売却」という選択肢を視野に入れていることが分かりました。

また、私自身も以前、開発してバズったサービスを売却しようと考えた時がありました。しかし、従来のWebメディア売買を主要取引としたサイト群では審査に落ちてしまい、結果的に商機を逃してしまったのかなと思っています。

何ができるの

ズバリ、売ったり買ったりです。

開発環境

  • Rails
  • Heroku
  • CloudFlare
  • Amazon s3
  • Amazon SES

売買スキームの実装

今回の売買では決済は実装していません。いずれ実装するかもしれませんが、まずは銀行口座によるマニュアルの振込も想定しています。交渉の申し込みから、契約内容の締結までを説明していきたいと思います。

交渉に関連するモデル

models/user.rb
class User < ApplicationRecord
  has_many :businesses, dependent: :destroy

  has_many :buy_negotiations,  class_name: "Negotiation",
                                  foreign_key: "buyer_id",
                                  dependent: :destroy
  has_many :sell_negotiations, class_name: "Negotiation",
                                  foreign_key: "seller_id",
                                  dependent:   :destroy
  has_many :buy_content, through: :buy_negotiations,  source: :buyer
  has_many :sell_content, through: :sell_negotiations, source: :seller
  has_many :messages
end
models/business.rb
class Business < ApplicationRecord
  belongs_to :user
  has_many :negotiations
end
models/negotiation.rb
class Negotiation < ApplicationRecord
  before_create :set_slug

  belongs_to :business
  has_many :messages

  belongs_to :buyer, class_name: "User"
  belongs_to :seller, class_name: "User"

  validates :buyer_id, presence: true
  validates :seller_id, presence: true

  def to_param
    slug
  end

  private

  def set_slug
    loop do
      self.slug = SecureRandom.uuid
      break unless Negotiation.where(slug: slug).exists?
    end
  end
end
models/message.rb
class Message < ApplicationRecord
  belongs_to :user
  belongs_to :negotiation
end

交渉(negotiation)は、URLを念の為スラッグで作るようにしています。

1) 交渉の申し込み

views/businesses/show.html.erb
<%= form_with model: @negotiation, url: negotiations_path, local: true do |f| %>
  <%= f.hidden_field :business_id, value: @business.id %>
  <%= f.hidden_field :seller_id, value: @business.user.id %>
  <%= f.hidden_field :buyer_id, value: current_user.id %>
  <%= f.submit "売却の交渉を始める", class: "btn btnPrimary btnStyle" %>
<% end %>
controllers/businesses_controller.rb
  def show
・・・
    @negotiation = @business.negotiations.build
  end

Businessに紐づいた、交渉(negotiation)を作っていきます。

controllers/negotiations_controller.rb
  def create
    @negotiation = Negotiation.create(create_params)
    if @negotiation.save
      NegotiationMailer.send_notification_to_seller(@negotiation).deliver
      flash[:success] = "交渉が始まりました!"
      redirect_to @negotiation
    else
      flash[:alert] = "交渉の開始に失敗しました。"
      redirect_to root_path
    end
  end

交渉が始まったら、売り手側にメールが飛ぶように設定しています。

2) 交渉

views/negotiations/show.html.erb
・・・
    <div class="chatBox">
      <%= render partial: 'messages/form', locals: { message: @message, negotiation: @negotiation, negotiation_id: @negotiation.id } %>
    </div>
    <div id="comments_area">
       <%= render partial: 'messages/index', locals: { messages: @messages } %>
    </div>
・・・
views/messages/_form.html.erb
<%= form_with(model: [negotiation, message] ) do |form| %>
  <div class="chatInput">
    <%= form.text_area :content, rows: "3" %>
  </div>
  <div class="chatHead">
    <%= form.submit "送信する" %>
  </div>
<% end %>

views/messages/_index.html.erb
<ul>
  <% messages.each do |message| %>
    <% unless message.id.nil? %>
      <li>
        <div class="msg">
          <div class="flexContainer">
            <div class="icon">
              <%= image_tag message.user.image.to_s %>
            </div>
            <div class="text">
              <div class="name">
                <span><%= message.user.username %></span>
              </div>
              <div class="content">
                <p><%= message.content %></p>
              </div>
            </div>
          </div>
        </div>
      </li>
    <% end %>
  <% end %>
</ul>
views/messages/index.js.erb
$("#comments_area").html("<%= j(render 'index', { messages: @message.negotiation.messages }) %>")
$("textarea").val('')

メッセージのやり取りはリアルタイム性を意識して、Firebaseなどの導入も考えたのですが、最初は実用最低限でいいかなと思って、普通のJSでやりました。

controllers/negotiations_controller.rb
  def show
    @negotiation = Negotiation.find_by_slug(params[:slug])
    @message = Message.new
    @messages = @negotiation.messages
  end
controllers/messages_controller.rb
  def create
    @message = @negotiation.messages.build(create_params)
    @message.user_id = current_user.id
    if @message.save
      MessageMailer.send_notification_to_receiver(@message).deliver
      render :index
    end
  end

negotiationの綴りを何回かタイポして、エラーが起こりました。
モデル名はもっと、なるべくシンプルな方がいいですね。

サービスの使い方はCrieitさんに投稿したのでこちらを参考にしてください。

いずれ、誰かが作るサービスだなと思っていましたが、であれば気づいた自分がまずトライすべきだなと思って、見切りでリリースしていました。すぐに売れることはなくても、とりあえず登録しておけば、忘れた頃に売れるかもしれません!

まずは、自分が作ったサービスに「値付け」から始めてみませんか?